diff --git a/exercises/practice/leap/.approaches/boolean-chain/content.md b/exercises/practice/leap/.approaches/boolean-chain/content.md new file mode 100644 index 00000000..30b5b439 --- /dev/null +++ b/exercises/practice/leap/.approaches/boolean-chain/content.md @@ -0,0 +1,29 @@ +# Chaining Boolean expressions + +```scheme +(define (leap-year? year) + (and (zero? (modulo year 4)) + (or (not (zero? (modulo year 100))) + (zero? (modulo year 400))))) +``` + +The Boolean expression `(zero? (modulo year 4))` checks the remainder from dividing `year` by 4. +If a year is evenly divisible by 4, the remainder will be zero. +[`(zero? n)`][zero] returns the Boolean value `#t` when `n` is zero. +All leap years are divisible by 4, and this pattern is then repeated whether a year is not divisible by 100 and whether it's divisible by 400. +[The `and` and `or` forms][and-or] can chain the values from these three Boolean expressions to produce a new value. +The `and` form produces `#f` if any expression is `#f` or it returns the value of the last expression. +Any value besides `#f` is equivalent to `#t`. +The `or` form produces `#f` if all expressions are `#f` or it returns the first non-`#f` value. + +| year | divisible by 4 | not divisible by 100 | divisible by 400 | result | +| ---- | -------------- | ------------------- | ---------------- | ------------ | +| 2020 | #t | #t | not evaluated | #t | +| 2019 | #f | not evaluated | not evaluated | #f | +| 2000 | #t | #f | #t | #t | +| 1900 | #t | #f | #f | #f | + +By situationally skipping some of the tests, we can efficiently calculate the result with fewer operations. + +[zero]: https://docs.racket-lang.org/reference/number-types.html#%28def._%28%28quote._~23~25kernel%29._zero~3f%29%29 +[and-or]: https://docs.racket-lang.org/guide/conditionals.html#%28part._and%2Bor%29 \ No newline at end of file diff --git a/exercises/practice/leap/.approaches/boolean-chain/snippet.txt b/exercises/practice/leap/.approaches/boolean-chain/snippet.txt new file mode 100644 index 00000000..37cae9eb --- /dev/null +++ b/exercises/practice/leap/.approaches/boolean-chain/snippet.txt @@ -0,0 +1,4 @@ +(define (leap-year? year) + (and (zero? (modulo year 4)) + (or (not (zero? (modulo year 100))) + (zero? (modulo year 400))))) \ No newline at end of file diff --git a/exercises/practice/leap/.approaches/cond-form/content.md b/exercises/practice/leap/.approaches/cond-form/content.md new file mode 100644 index 00000000..4d69db83 --- /dev/null +++ b/exercises/practice/leap/.approaches/cond-form/content.md @@ -0,0 +1,46 @@ +# Cond form + +```scheme +(define (leap-year? year) + (cond + [(zero? (modulo year 400)) #t] + [(zero? (modulo year 100)) #f] + [(zero? (modulo year 4)) #t] + [else #f])) +``` + +[The cond form][cond-form] sequentially evaluates a series of test expressions until one produces a non-`#f` value. +Then the associated body for the test expression is evaluated, producing a value. +At this point, test expression evaluation inside the cond form ends and the value is returned. +If no test expressions are matched, [void][void-constant] is produced so often +the last expression is `#t` or `else` which will always be satisfied, allowing a default value to be set. + +The cond form can be visualized as a nested [if form][if-form]. + +```scheme +(define (leap-year? year) + (if (zero? (modulo year 400)) + #t + (if (zero? (modulo year 100)) + #f + (if (zero? (modulo year 4)) + #t + #f)))) +``` + +To run though how this works, `year` is set to 2024, a leap year. + +```scheme +(zero? (modulo year 400)) ; produces #f so ignored +(zero? (modulo year 100)) ; produces #f so ignored +(zero? (modulo year 4)) ; produces #t +``` + +The third test expression produces the first non-`#f` value so its body gets evaluated. +That body contains `#t` which is then the returned value from the cond. +That value is then returned by the function. + + +[cond-form]: https://docs.racket-lang.org/guide/conditionals.html#%28part._cond%29 +[void-constant]: https://docs.racket-lang.org/reference/void.html#%28def._%28%28quote._~23~25kernel%29._void%29%29 +[if-form]: https://docs.racket-lang.org/reference/if.html \ No newline at end of file diff --git a/exercises/practice/leap/.approaches/cond-form/snippet.txt b/exercises/practice/leap/.approaches/cond-form/snippet.txt new file mode 100644 index 00000000..1dc9e0c6 --- /dev/null +++ b/exercises/practice/leap/.approaches/cond-form/snippet.txt @@ -0,0 +1,6 @@ +(define (leap-year? year) + (cond + [(zero? (modulo year 400)) #t] + [(zero? (modulo year 100)) #f] + [(zero? (modulo year 4)) #t] + [else #f])) \ No newline at end of file diff --git a/exercises/practice/leap/.approaches/config.json b/exercises/practice/leap/.approaches/config.json new file mode 100644 index 00000000..d90b373f --- /dev/null +++ b/exercises/practice/leap/.approaches/config.json @@ -0,0 +1,36 @@ +{ + "introduction": { + "authors": [ + "BNAndras" + ] + }, + "approaches": [ + { + "uuid": "b163b6d5-92ed-4073-8169-03e2a1b09fab", + "slug": "boolean-chain", + "title": "Boolean chain", + "blurb": "Use operators to check boolean values in a chain", + "authors": [ + "BNAndras" + ] + }, + { + "uuid": "22229478-54ce-48ec-867f-1adcf04c42ac", + "slug": "cond-form", + "title": "Cond", + "blurb": "Use the cond form to check boolean values sequentially.", + "authors": [ + "BNAndras" + ] + }, + { + "uuid": "45f28b96-4803-424c-9973-f2b4445f7227", + "slug": "pattern-matching", + "title": "Pattern matching", + "blurb": "Use the match form to check a list of boolean values at once.", + "authors": [ + "BNAndras" + ] + } + ] +} \ No newline at end of file diff --git a/exercises/practice/leap/.approaches/introduction.md b/exercises/practice/leap/.approaches/introduction.md new file mode 100644 index 00000000..ba7387e0 --- /dev/null +++ b/exercises/practice/leap/.approaches/introduction.md @@ -0,0 +1,51 @@ +# Introduction + +There are several idiomatic ways to solve this exercise. + +## General guidance + +Regardless of the approach chosen, this exercise requires students to use Boolean logic to decide if a given year is a leap year. + +## Approach: Boolean chaining + +```scheme +(define (leap-year? year) + (and (zero? (modulo year 4)) + (or (not (zero? (modulo year 100))) + (zero? (modulo year 400))))) +``` + +For more information, check the [Boolean chain approach][approach-boolean-chain]. + +## Approach: Cond form + +```scheme +(define (leap-year? year) + (cond + [(zero? (modulo year 400)) #t] + [(zero? (modulo year 100)) #f] + [(zero? (modulo year 4)) #t] + [else #f])) +``` + +For more information, check the [cond form approach][approach-cond-form]. + +## Approach: Pattern matching + +```scheme +(define (leap-year? year) + (define divisible-by-4 (zero? (modulo year 4))) + (define divisible-by-100 (zero? (modulo year 100))) + (define divisible-by-400 (zero? (modulo year 400))) + (match (list divisible-by-4 divisible-by-100 divisible-by-400) + [(list _ _ #t) #t] + [(list _ #t _) #f] + [(list #t _ _) #t] + [_ #f])) +``` + +For more information, check the [pattern matching approach][approach-pattern-matching]. + +[approach-boolean-chain]: https://exercism.org/tracks/racket/exercises/leap/approaches/boolean-chain +[approach-cond-form]: https://exercism.org/tracks/racket/exercises/leap/cond-form +[approach-pattern-matching]: https://exercism.org/tracks/racket/exercises/leap/approaches/pattern-matching \ No newline at end of file diff --git a/exercises/practice/leap/.approaches/pattern-matching/content.md b/exercises/practice/leap/.approaches/pattern-matching/content.md new file mode 100644 index 00000000..1096838e --- /dev/null +++ b/exercises/practice/leap/.approaches/pattern-matching/content.md @@ -0,0 +1,51 @@ +# Pattern matching + +```scheme +(define (leap-year? year) + (define divisible-by-4 (zero? (modulo year 4))) + (define divisible-by-100 (zero? (modulo year 100))) + (define divisible-by-400 (zero? (modulo year 400))) + (match (list divisible-by-4 divisible-by-100 divisible-by-400) + [(list _ _ #t) #t] + [(list _ #t _) #f] + [(list #t _ _) #t] + [_ #f])) +``` + +[The match form][match-form] compares an expression sequentially to a series of clauses that contain patterns describing the contents of that expression and values returned when a given pattern is matched. +For this exercise, this takes the form of creating a list containing three Boolean values representing whether a year is divisible by 4, 100, or 400. +As an example, let's run through this code assuming `year` is set to 1993. +`(divisible-by-4 1999)` is `#f`, `(divisible-by-100 1999)` is `#f`, and `(divisible-by-400 1999)` is `#f`. +Filling in these results below, we have four patterns underneath it: + +```scheme +(match (list #t #f #f) ; assuming the year is 1999 + [(list _ _ #t) #t] + [(list _ #t _) #f] + [(list #t _ _) #t] + [_ #f]) +``` + +The first three expect a list of three elements (`_` matching any value), but they differ on which value in the pattern can only be the literal value `#t`. + +The first clause `[(list _ _ #t) #t]` describes a series of checks where the year must at least be divisible by 400. +A year divisible by 400 is also divisible by 4 and 100 so we can safely assume all three checks returned `#t` and there's no need to specify the result. +If this pattern is matched, `#t` is returned. +1999 isn't divisible by 400 since the `#f` in the third position means the pattern isn't matched. + +The second clause `[(list _ #t _) #f]` describes a series of checks where the year must at least be divisible by 100. +A year divisible by 100 is also divisible by 4 so we can be sure that other check returned true and there's no need to specify it here. +We can also assume a year divisible by 100 wouldn't be divisible by 400 because if it were, the previous pattern would have already matched. +If this pattern is matched, `#f` is returned. +1999 isn't divisible by 100 since `#f` in the second position means the pattern isn't matched. + +The third clause `[(list #t _ _) #t]` describes a series of checks where the year must at least be divisible by 4. +If this pattern is matched, `#t` is returned. +1999 isn't divisible by 4 since `#f` in the first position means the pattern isn't matched. + +The fourth clause `[_ #f]` features a pattern with a single `_`, which means any value at all can match this pattern. +Without such a clause, an exception will occur if none of the clauses' patterns matched. +1999 isn't divisible by 4, 100, or 400, but it matches this catch-all pattern after not matching the previous patterns. +As a result, `#f` is returned by the code. + +[match-form]: https://docs.racket-lang.org/guide/match.html diff --git a/exercises/practice/leap/.approaches/pattern-matching/snippet.txt b/exercises/practice/leap/.approaches/pattern-matching/snippet.txt new file mode 100644 index 00000000..132eb922 --- /dev/null +++ b/exercises/practice/leap/.approaches/pattern-matching/snippet.txt @@ -0,0 +1,6 @@ +(define (leap-year? year) + (match (list (zero? (modulo year 4)) (zero? (modulo year 100)) (zero? (modulo year 400))) + [(list _ _ #t) #t] + [(list _ #t _) #f] + [(list #t _ _) #t] + [_ #f])) \ No newline at end of file