-
-
Notifications
You must be signed in to change notification settings - Fork 40
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 leap approaches #304
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
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
54 changes: 54 additions & 0 deletions
54
exercises/practice/leap/.approaches/boolean-chain/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,54 @@ | ||
# Chain of boolean expressions | ||
|
||
```r | ||
leap <- function(year) { | ||
year %% 4 == 0 && (year %% 100 != 0 || year %% 400 == 0) | ||
} | ||
``` | ||
|
||
The first boolean expression uses the [modulo operator][arithmetic-operators] (`%%`) to check if the year is evenly divided by `4`. | ||
- If the year is not evenly divisible by `4`, then the chain will "short circuit" due to the next operator being a [logical AND][logical-operators] `&`), and will return `FALSE`. | ||
- If the year _is_ evenly divisible by `4`, then the year is checked to _not_ be evenly divisible by `100`. | ||
- If the year is not evenly divisible by `100`, then the expression is `TRUE` and the chain will "short-circuit" to return `TRUE`, | ||
since the next operator is a [logical OR][logical-operators] (`|`). | ||
- If the year _is_ evenly divisible by `100`, then the expression is `FALSE`, and the returned value from the chain will be if the year is evenly divisible by `400`. | ||
|
||
| year | year %% 4 == 0 | year %% 100 != 0 | year %% 400 == 0 | is leap year | | ||
| ---- | ------------- | --------------- | --------------- | ------------ | | ||
| 2020 | TRUE | TRUE | not evaluated | TRUE | | ||
| 2019 | FALSE | not evaluated | not evaluated | FALSE | | ||
| 2000 | TRUE | FALSE | TRUE | TRUE | | ||
| 1900 | TRUE | FALSE | FALSE | FALSE | | ||
|
||
|
||
The chain of boolean expressions is efficient, as it proceeds from testing the most likely to least likely conditions. | ||
It is the fastest approach when testing a year that is not evenly divisible by `100` and is not a leap year. | ||
|
||
## Refactoring | ||
|
||
By using the [falsiness][logical-vectors] of `0`, the [not operator][logical-operators] (`!`) can be used instead of comparing equality to `0`. | ||
For example: | ||
|
||
```r | ||
leap <- function(year) { | ||
!year %% 4 && (year %% 100 != 0 || !year %% 400) | ||
} | ||
``` | ||
|
||
It can be thought of as the expression _not_ having a remainder. While it may harm readability (this is of course subjective), based on benchmarking this is slightly faster than checking whether the remainder equals zero. | ||
|
||
## Vectorised implementation | ||
|
||
In R many operations are vectorised (by default), or can be vectorised. `|` and `&` (by contrast to `||` and `&&`) are vectorised versions of [logical operators][logical-operators] which could also be used if one wanted to allow for a vector of years as input. | ||
|
||
```r | ||
leap <- function(year) { | ||
year %% 4 == 0 & (year %% 100 != 0 | year %% 400 == 0) | ||
} | ||
``` | ||
|
||
In general with vectorised operations the trade-off is that it tends to be slower for operations on vectors of length 1 (R doesn't have an atomic scalar data type so scalars are still just vectors of length 1), but in many cases vectorised implementations can be more efficient than naive looping when dealing with multiple input values, and the difference can be quite dramatic for more complex computations. | ||
|
||
[arithmetic-operators]: https://stat.ethz.ch/R-manual/R-patched/library/base/html/Arithmetic.html | ||
[logical-operators]: https://stat.ethz.ch/R-manual/R-devel/library/base/html/Logic.html | ||
[logical-vectors]: https://stat.ethz.ch/R-manual/R-devel/library/base/html/logical.html |
3 changes: 3 additions & 0 deletions
3
exercises/practice/leap/.approaches/boolean-chain/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,3 @@ | ||
leap <- function(year) { | ||
year %% 4 == 0 && (year %% 100 != 0 || year %% 400 == 0) | ||
} |
45 changes: 45 additions & 0 deletions
45
exercises/practice/leap/.approaches/conditional-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,45 @@ | ||
# Conditional expression | ||
|
||
```r | ||
leap <- function(year) { | ||
if (year %% 100 == 0) | ||
year %% 400 == 0 | ||
else | ||
year %% 4 == 0 | ||
} | ||
``` | ||
|
||
A [conditional expression][control-flow] uses a maximum of two checks to determine if a year is a leap year. | ||
|
||
It starts by testing the outlier condition of the year being evenly divisible by `100`. | ||
It does this by using the [modulo operator][arithmetic-operators] (`%%`) and checking whether the remainder is `0`. | ||
|
||
- If the year is evenly divisible by `100`, then the expression is `TRUE`, and the conditional expression returns if the year is evenly divisible by `400`. | ||
- If the year is _not_ evenly divisible by `100`, then the expression is `FALSE`, and the conditional expression returns if the year is evenly divisible by `4`. | ||
|
||
| year | year %% 100 == 0 | year %% 400 == 0 | year %% 4 == 0 | is leap year | | ||
| ---- | --------------- | --------------- | -------------- | ------------ | | ||
| 2020 | FALSE | not evaluated | TRUE | TRUE | | ||
| 2019 | FALSE | not evaluated | FALSE | FALSE | | ||
| 2000 | TRUE | TRUE | not evaluated | TRUE | | ||
| 1900 | TRUE | FALSE | not evaluated | FALSE | | ||
|
||
Although it uses a maximum of only two checks, the conditional expression tests an outlier condition first, | ||
making it less efficient than another approach that would first test if the year is evenly divisible by `4`, | ||
which is more likely than the year being evenly divisible by `100`. | ||
The conditional expression was fastest in benchmarking when the year was a leap year or was evenly divisible by `100`, | ||
but those are the least likely conditions. | ||
|
||
In R many operations are vectorised (by default), or can be vectorised. [`ifelse`][ifelse] is a vectorised version of `if ... else ...`, which could also be used if one wanted to allow for a vector of years as input. | ||
|
||
```r | ||
leap <- function(year) { | ||
ifelse(year %% 100 == 0, year %% 400 == 0, year %% 4 == 0) | ||
} | ||
``` | ||
|
||
In general with vectorised operations the trade-off is that it tends to be slower for operations on vectors of length 1 (R doesn't have an atomic scalar data type so scalars are still just vectors of length 1), but in many cases vectorised implementations can be more efficient than naive looping when dealing with multiple input values, and the difference can be quite dramatic for more complex computations. | ||
|
||
[control-flow]: https://stat.ethz.ch/R-manual/R-patched/library/base/html/Control.html | ||
[arithmetic-operators]: https://stat.ethz.ch/R-manual/R-patched/library/base/html/Arithmetic.html | ||
[ifelse]: https://stat.ethz.ch/R-manual/R-patched/library/base/html/ifelse.html |
6 changes: 6 additions & 0 deletions
6
exercises/practice/leap/.approaches/conditional-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,6 @@ | ||
leap <- function(year) { | ||
if (year %% 100 == 0) | ||
year %% 400 == 0 | ||
else | ||
year %% 4 == 0 | ||
} |
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,23 @@ | ||
{ | ||
"introduction": { | ||
"authors": [ | ||
"jonmcalder" | ||
] | ||
}, | ||
"approaches": [ | ||
{ | ||
"uuid": "6fb5f80f-706c-4bbb-ba3e-cd53bca110bd", | ||
"slug": "boolean-chain", | ||
"title": "Boolean chain", | ||
"blurb": "Use a chain of boolean expressions.", | ||
"authors": ["jonmcalder"] | ||
}, | ||
{ | ||
"uuid": "aa5cd568-60e7-4d9d-9573-752ecdbb92f7", | ||
"slug": "conditional-expression", | ||
"title": "Conditional expression", | ||
"blurb": "Use an if / else statement.", | ||
"authors": ["jonmcalder"] | ||
} | ||
] | ||
} |
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,48 @@ | ||
# Introduction | ||
|
||
There are various idiomatic approaches to solve Leap. | ||
You can use a chain of boolean expressions to test the conditions. | ||
Or you can use a [conditional expression][control-flow]. | ||
|
||
## General guidance | ||
|
||
The key to solving Leap is to know if the year is evenly divisible by `4`, `100` and `400`. | ||
For determining that, you will use the [modulo operator][arithmetic-operators] (`%%`) to check whether the remainder is `0`. | ||
|
||
## Approach: Chain of Boolean expressions | ||
|
||
```r | ||
leap <- function(year) { | ||
year %% 4 == 0 && (year %% 100 != 0 || year %% 400 == 0) | ||
} | ||
``` | ||
|
||
For more information, check the [Boolean chain approach][approach-boolean-chain]. | ||
|
||
## Approach: Conditional expression | ||
|
||
```r | ||
leap <- function(year) { | ||
if (year %% 100 == 0) | ||
year %% 400 == 0 | ||
else | ||
year %% 4 == 0 | ||
} | ||
``` | ||
|
||
For more information, check the [conditional expression approach][approach-conditional-expression]. | ||
|
||
## Which approach to use? | ||
|
||
- The chain of boolean expressions should be the most efficient, as it proceeds from the most likely to least likely conditions. | ||
It has a maximum of three checks. | ||
It is the fastest approach when testing a year that is not evenly divisible by `100` and is not a leap year. | ||
Since most years fit those conditions, it is the most efficient approach overall. | ||
- The conditional expression approach has a maximum of only two checks, but it starts from a less likely condition. | ||
The conditional expression approach was faster in benchmarking when the year was a leap year or was evenly divisible by `100`, | ||
but those are the least likely conditions. | ||
|
||
[control-flow]: https://stat.ethz.ch/R-manual/R-patched/library/base/html/Control.html | ||
[arithmetic-operators]: https://stat.ethz.ch/R-manual/R-patched/library/base/html/Arithmetic.html | ||
[approach-boolean-chain]: https://exercism.org/tracks/r/exercises/leap/approaches/boolean-chain | ||
[approach-conditional-expression]: https://exercism.org/tracks/r/exercises/leap/approaches/conditional-expression |
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.
[conditional expression][conditional-expression]
at line 5 is a broken link, needs a reference at the bottom of the file.