-
-
Notifications
You must be signed in to change notification settings - Fork 111
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add approaches for Leap * Update config.json for leap approaches * Add links and backticks * Format and update uuids * Reduce snippet lengths as per linting rules * Fix links and copy * Use "expression" instead of "statement" * Update exercises/practice/leap/.approaches/case-expression/content.md Co-authored-by: Jie <jie.gillet@gmail.com> * Update exercises/practice/leap/.approaches/if-expression/content.md Co-authored-by: Jie <jie.gillet@gmail.com> * Update exercises/practice/leap/.approaches/if-expression/content.md Co-authored-by: Jie <jie.gillet@gmail.com> * Address code review comments * Address code review comments * Update exercises/practice/leap/.approaches/case-expression/content.md Co-authored-by: Jie <jie.gillet@gmail.com> * Update code for if-expression approach --------- Co-authored-by: Jie <jie.gillet@gmail.com>
- Loading branch information
1 parent
5949b5e
commit 639e59a
Showing
8 changed files
with
275 additions
and
0 deletions.
There are no files selected for viewing
38 changes: 38 additions & 0 deletions
38
exercises/practice/leap/.approaches/case-expression/content.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
# Case expression | ||
|
||
```elm | ||
isLeapYear : Int -> Bool | ||
isLeapYear year = | ||
let | ||
isDivisibleByFour = (modBy 4 year) == 0 | ||
isDivisibleBy100 = (modBy 100 year) == 0 | ||
isDivisibleBy400 = (modBy 400 year) == 0 | ||
in | ||
case (isDivisibleByFour, isDivisibleBy100, isDivisibleBy400) of | ||
(True, _, True) -> True | ||
(True, False, _) -> True | ||
_ -> False | ||
``` | ||
|
||
## In this approach | ||
|
||
In this approach we use a [`case` expression][case-expression], and match on a [`Tuple`][tuple]. | ||
It turns out that, if we are careful to ask questions in the right order, we can always potentially attain certainty about the answer by asking one more question. | ||
This is a [truth-table][truth-table] like approach, which can be easier to read for complicated logic. | ||
|
||
## When to use a case expression? | ||
|
||
Using a case expression with a `Tuple` is idiomatic in Elm, but tuples have a maximum of 3 values, so can't be used for anything larger than this. | ||
|
||
Strings and lists can hold more values and can also be used with case expressions, which are useful in many circumstances. | ||
Using a list with a case expression and recursion is especially common. | ||
|
||
[case-expression]: | ||
https://elmprogramming.com/case-expression.html | ||
"case expressions in Elm" | ||
[tuple] | ||
https://elmprogramming.com/tuple.html | ||
"Tuples in Elm" | ||
[truth-table]: | ||
https://brilliant.org/wiki/truth-tables/ | ||
"Truth tables" |
4 changes: 4 additions & 0 deletions
4
exercises/practice/leap/.approaches/case-expression/snippet.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
case (isDivisibleByFour, isDivisibleBy100, isDivisibleBy400) of | ||
(True, _, True) -> True | ||
(True, False, _) -> True | ||
_ -> False |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
{ | ||
"introduction": { | ||
"authors": [ | ||
"ceddlyburge" | ||
] | ||
}, | ||
"approaches": [ | ||
{ | ||
"uuid": "243cf336-7c0b-430b-a064-c976adea8d70", | ||
"slug": "logical-expression", | ||
"title": "Logical expression", | ||
"blurb": "Use logical operators to combine several tests into one.", | ||
"authors": [ | ||
"ceddlyburge" | ||
] | ||
}, | ||
{ | ||
"uuid": "31ad676e-299b-4b7a-912c-3e4f68b5d873", | ||
"slug": "case-expression", | ||
"title": "Case expression", | ||
"blurb": "Use a case expression.", | ||
"authors": [ | ||
"ceddlyburge" | ||
] | ||
}, | ||
{ | ||
"uuid": "9546e09d-9cde-4da1-b78a-0ef33d7d6db3", | ||
"slug": "if-expression", | ||
"title": "If expression", | ||
"blurb": "Use an if expression.", | ||
"authors": [ | ||
"ceddlyburge" | ||
] | ||
} | ||
] | ||
} |
50 changes: 50 additions & 0 deletions
50
exercises/practice/leap/.approaches/if-expression/content.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
# If expression | ||
|
||
```elm | ||
isLeapYear : Int -> Bool | ||
isLeapYear year = | ||
let | ||
divisibleBy number = | ||
modBy number year == 0 | ||
in | ||
if divisibleBy 100 then | ||
divisibleBy 400 | ||
|
||
else | ||
divisibleBy 4 | ||
``` | ||
|
||
An [`if` expression][if-expression] (`if … then … else …`) is a compound expression that uses a test to determine which of two alternatives to evaluate to. | ||
Many other languages feature a similar construct, often termed 'ternary operator'. | ||
|
||
## In this approach | ||
|
||
This approach uses exactly two tests to determine whether a year is a leap year. | ||
|
||
The first test is for divisibility by 100. | ||
Once we know if the year is a multiple of 100, we know which further test to perform. | ||
|
||
- If the year is evenly divisible by 100, then `divisibleBy 100` will evaluate to `True` and the entire `if` expression will evaluate to whatever `divisibleBy 400` evaluates to. | ||
- If the year is _not_ evenly divisible by 100, then `divisibleBy 100` is `False` and so the `if` expression evaluates to `divisibleBy 4`. | ||
|
||
This approach is not as concise as the [logical-expression][logical-expression], but it is [easier to describe][describable-code], which makes it easier to comunicate the problem domain. | ||
|
||
## When to use `if`? | ||
|
||
[`if` expressions][if-expression] might be a good fit when you | ||
|
||
- need an expression that | ||
- chooses between exactly two options | ||
- depending on a single `Bool`. | ||
|
||
When you have something other than a `Bool`, use `case` instead. | ||
|
||
[if-expression]: | ||
https://elm-lang.org/docs/syntax#conditionals | ||
"if expressions in Elm" | ||
[logical-expression]: | ||
https://exercism.org/tracks/elm/exercises/leap/approaches/conditional-expression | ||
"Approach: a conditional expression" | ||
[describable-code]: | ||
https://www.freecodecamp.org/news/writing-describable-code/ | ||
"Writing easily describable code" |
5 changes: 5 additions & 0 deletions
5
exercises/practice/leap/.approaches/if-expression/snippet.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
if divisibleBy 100 then | ||
divisibleBy 400 | ||
|
||
else | ||
divisibleBy 4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
# Introduction | ||
|
||
There are various idiomatic approaches to solve Leap. | ||
All approaches listed below check for divisibility by 4, 100, and 400. | ||
However, they differ in the ways in which they combine these checks. | ||
|
||
## Approach: a logical expression | ||
|
||
```elm | ||
isLeapYear : Int -> Bool | ||
isLeapYear year = | ||
let | ||
divisibleBy number = | ||
modBy number year == 0 | ||
in | ||
divisibleBy 4 && (not (divisibleBy 100) || divisibleBy 400) | ||
``` | ||
|
||
A logicial expression is the most concise approach. | ||
[Read more about this approach][logical-expression]. | ||
|
||
## Approach: an if expression | ||
|
||
```elm | ||
isLeapYear : Int -> Bool | ||
isLeapYear year = | ||
let | ||
divisibleBy number = | ||
modBy number year == 0 | ||
in | ||
if divisibleBy 100 then | ||
divisibleBy 400 | ||
|
||
else | ||
divisibleBy 4 | ||
``` | ||
|
||
An if expression is not as concise, but is [easier to describe][describable-code]. | ||
[Read more about this approach][if-expression]. | ||
|
||
## Approach: a case expression | ||
|
||
```elm | ||
isLeapYear : Int -> Bool | ||
isLeapYear year = | ||
let | ||
isDivisibleByFour = (modBy 4 year) == 0 | ||
isDivisibleBy100 = (modBy 100 year) == 0 | ||
isDivisibleBy400 = (modBy 400 year) == 0 | ||
in | ||
case (isDivisibleByFour, isDivisibleBy100, isDivisibleBy400) of | ||
(True, _, True) -> True | ||
(True, False, _) -> True | ||
_ -> False | ||
``` | ||
|
||
This takes a [truth-table][truth-table] like approach, which can be easier to read for complicated logic. | ||
[Read more about this approach][case-expression]. | ||
|
||
## Other approaches | ||
|
||
There are also more esoteric approaches, [such as this one using recursion, a list and a case expression][esoteric-approach], that are not idiomatic, but that can be interesting to look at, and probably come about by trying to solve the problem using some artificial constraints, as you would often do in a code kata. | ||
|
||
## General guidance | ||
|
||
The key to determining whether a given year is a leap year is to know whether the year is evenly divisible by `4`, `100`, and `400`. | ||
For determining that, you can use the [`modBy` function][modby-function], which yields the remainder after division. | ||
|
||
You don't need to use a ['let' expression][let-expression] to define an `isDivisibleBy` function, although all the examples here do to aid readability. | ||
|
||
## Which approach to use? | ||
|
||
Code exists primarily for humans to read and reason about. | ||
Therefore, in general, go with the approach that _makes the most sense_. | ||
|
||
All approaches listed here are valid choices unless marked otherwise. | ||
|
||
[logical-expression]: | ||
https://exercism.org/tracks/elm/exercises/leap/approaches/logical-expression | ||
"Approach: a conditional expression" | ||
[if-expression]: | ||
https://exercism.org/tracks/elm/exercises/leap/approaches/if-expression | ||
"Approach: an if expression" | ||
[case-expression]: | ||
https://exercism.org/tracks/elm/exercises/leap/approaches/case-expression | ||
"Approach: a case expression" | ||
[describable-code]: | ||
https://www.freecodecamp.org/news/writing-describable-code/ | ||
"Writing easily describable code" | ||
[truth-table]: | ||
https://brilliant.org/wiki/truth-tables/ | ||
"Truth tables" | ||
[esoteric-approach] | ||
https://exercism.org/tracks/elm/exercises/leap/solutions/edgerunner | ||
"An esoteric solution to leap, using recursion, a list and a case expression" | ||
[modby-function] | ||
https://package.elm-lang.org/packages/elm/core/latest/Basics#modBy | ||
"modBy function in Elm" | ||
[let-expression] | ||
https://elm-lang.org/docs/syntax#let-expressions | ||
"let expressions in Elm" |
34 changes: 34 additions & 0 deletions
34
exercises/practice/leap/.approaches/logical-expression/content.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# Logical expression | ||
|
||
```elm | ||
isLeapYear : Int -> Bool | ||
isLeapYear year = | ||
let | ||
divisibleBy number = | ||
modBy number year == 0 | ||
in | ||
divisibleBy 4 && (not (divisibleBy 100) || divisibleBy 400) | ||
``` | ||
|
||
We can combine smaller logical expressions into larger ones using the logical operators `&&` (and), `||` (or), and `not` (negation). | ||
|
||
A logicial expression is the most concise approach. | ||
|
||
## Precedence | ||
|
||
In school they teach you that `2 + 3 * 4` is to be read as meaning `2 + (3 * 4)`. | ||
This is a convention, chosen for its convenience. | ||
We say that the `*` operator has _higher [precedence][precedence]_ than `+`. | ||
|
||
In logic similar ambiguities exist, and these are similarly resolved. | ||
|
||
- _and_ has higher precedence than _or_, and | ||
- _not_ has higher precedence than both _and_ and _or_. | ||
|
||
For example, `p || q && r` means the same as `p || (q && r)`. | ||
|
||
If you don't wish to remember the precendence of all the things, or force readers of your code to do so, you can use parentheses to make it explicit. | ||
|
||
[precedence]: | ||
https://en.wikipedia.org/wiki/Order_of_operations | ||
"Wikipedia: Order of operations" |
7 changes: 7 additions & 0 deletions
7
exercises/practice/leap/.approaches/logical-expression/snippet.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
isLeapYear : Int -> Bool | ||
isLeapYear year = | ||
let | ||
divisibleBy number = | ||
modBy number year == 0 | ||
in | ||
divisibleBy 4 && (not (divisibleBy 100) || divisibleBy 400) |