-
-
Notifications
You must be signed in to change notification settings - Fork 111
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add approaches for Leap #643
Merged
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
0dce4a8
Add approaches for Leap
ceddlyburge 8411f88
Update config.json for leap approaches
ceddlyburge 607bfa3
Add links and backticks
ceddlyburge 02dc0eb
Format and update uuids
ceddlyburge 6803c16
Reduce snippet lengths as per linting rules
ceddlyburge 438b540
Fix links and copy
ceddlyburge 9c2ce5c
Use "expression" instead of "statement"
ceddlyburge 0ef0849
Update exercises/practice/leap/.approaches/case-expression/content.md
ceddlyburge 58c5c79
Update exercises/practice/leap/.approaches/if-expression/content.md
ceddlyburge 5cba4b0
Update exercises/practice/leap/.approaches/if-expression/content.md
ceddlyburge 8e37323
Address code review comments
ceddlyburge 73b4ed4
Address code review comments
ceddlyburge a022ccd
Update exercises/practice/leap/.approaches/case-expression/content.md
ceddlyburge 3c97b91
Update code for if-expression approach
ceddlyburge File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That really is esoteric 😆 |
||
[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) |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't match the actual code, since we start by checking 400.
From the description, the code would probably be (untested)
which only has one if statement. If you don't want to change the description, I would consider changing to this code (after double checking that it passes the tests).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does pass the tests, I'll use it ...