Skip to content

Commit

Permalink
Leap approaches WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
BNAndras committed Jan 15, 2024
1 parent edff34d commit 42256b9
Show file tree
Hide file tree
Showing 8 changed files with 227 additions and 0 deletions.
32 changes: 32 additions & 0 deletions exercises/practice/leap/.approaches/cond/content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Conditional Chaining

The [`cond`] form chains a series of tests to select an expression and then evaluate it, returning the result.
Each test is evaluated in order until a test produces a true value, at which point its associated body is evaluated.
The value from the body is then returned, and no more tests get evaluated inside the cond form.

If the `cond` form sounds like the [`if`] form, that's because at runtime, the `cond` operates much like a nested `if`.

Compare

```scheme
(cond
[(zero? (modulo year 400)) #t]
[(positive? (modulo year 100)) #f]
[(zero? (modulo year 4)) #t]
[else #f]))
```

to

```scheme
(if (zero? (modulo year 400))
#t
(if (positive? (modulo year 100))
#f
(if (zero? (modulo year 4))
#t
#f)))
```

[cond]: https://docs.racket-lang.org/guide/conditionals.html#%28part._cond%29
[if]: https://docs.racket-lang.org/reference/if.html
6 changes: 6 additions & 0 deletions exercises/practice/leap/.approaches/cond/snippet.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
(define (leap-year? year)
(cond
[(zero? (modulo year 400)) #t]
[(positive? (modulo year 100)) #f]
[(zero? (modulo year 4)) #t]
[else #f]))
36 changes: 36 additions & 0 deletions exercises/practice/leap/.approaches/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"introduction": {
"authors": [
"BNAndras"
]
},
"approaches": [
{
"uuid": "be6d6c6e-8e19-4657-aad5-3382e7ec01db",
"slug": "operators",
"title": "Boolean Operators",
"blurb": "Use boolean operators to combine the checks.",
"authors": [
"BNAndras"
]
},
{
"uuid": "428e3cee-309a-4c45-a6d4-3bff4eb41daa",
"slug": "cond",
"title": "Cond",
"blurb": "Use `cond` to control order of checks.",
"authors": [
"BNAndras"
]
},
{
"uuid": "129e6863-275b-4fa3-abfe-4cb07c97acae",
"slug": "match",
"title": "Match",
"blurb": "Use `match` and lists to decide if the year is a leap one.",
"authors": [
"BNAndras"
]
}
]
}
Empty file.
36 changes: 36 additions & 0 deletions exercises/practice/leap/.approaches/match/content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Pattern matching

Racket supports pattern matching through the use of the [match form](match).
The `match` form compares an expression sequentially to a series of clauses containing patterns.
Each clause contains a pattern and a body, and the first clause whose pattern matches the expression has their body evaluated.

Let's see how the `match form` works with a list of values.
In the following example, `_` is a wildcard and can match any value.
So in the first clause, the pattern is looking for a list of three elements and the last value must be a literal `#t`.

```scheme
(match `(1 #t #f)
[(list #t _ _) "first"]
[(list _ #f _) "second"]
[(list _ _ #f) "third"])
; "third"
```

The first clause isn't matched since the first element is 1, not `#t`.
The second clause isn't matched since the second element is `#f`, not `#t`.
The third clause is matched since the third element is `#f`.
Therefore, we evaluate the body of the third clause and return the string `"third"`.

If no clauses matched, an exception is raised so the `else` syntax can be used to provide a default expression if no other clauses match.
In the following example, the third cause no longer matches our value so the else is evaluated.

```scheme
(match `(1 #t 1)
[(list #t _ _) "first"]
[(list _ #f _) "second"]
[(list _ _ #f) "third"])
[else "whoops"]
; "whoops"
```
8 changes: 8 additions & 0 deletions exercises/practice/leap/.approaches/match/snippet.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
(define (leap-year? year)
(define by4 (zero? (modulo year 4)))
(define by100 (positive? (modulo year 100)))
(define by400 (zero? (modulo year 400)))
(match (list by4 by100 by400)
[(list _ _ #t) #t]
[(list _ #t _) #f]
[(list #t _ _) #t]))
105 changes: 105 additions & 0 deletions exercises/practice/leap/.approaches/operators/content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Combining Tests

In Racket, [the `and` and `or` forms](and-or) can combine two expressions to produce a new value. Both forms can "short-circuit" or skip evaluation of the second expression depending on the result of the first expression's evaluation. Remember any value besides `#f` (false) is equivalent to `#t` (true).

## And

The `and` form returns the evaluated value of the first expression if it's false. However, if the first expression is true, Racket only then evaluates the second expression and returns this second value.

```scheme
(and #f #t) ; #f
(and #t #f) ; #f
(and #t #t) ; #t
(and 1 2) ; 2
```

## Or

The `or` form returns the evaluated value of the first expression that is true.
The second expression is only evaluated when the first expression is false.

```scheme
(or #f #t) ; #t
(or #t #f) ; #t
(or #t #t) ; #t
(or 1 2) ; 1
```

## Leap Year

To check if `year` is a leap year, we can combine these two forms.
For example, we can start with our base case that a leap year is evenly divisible by 4 so `(zero? (modulo year 4))` produces `#t` when a year is evenly divisible by 4 (no remainder left).
We can exclude years not evenly divisible by 400 with `(positive? (modulo year 100)` which returns #t when there's a positive remainder left after dividing year by 100.
Using the `and` form, we can first check the base case and only evaluate the second check when we're dealing with a potential leap year.

Let's pretend we're checking the year 1900.

```scheme
(and (zero? (modulo year 4))
(positive? (modulo year 100)))
```

```scheme
(and (zero? (modulo 1900 4))
(positive? (modulo year 100)))
```

```scheme
(and (zero? 0)
(positive? (modulo year 100)))
```

```scheme
(and #t
(positive? (modulo year 100)))
```

...

```scheme
(and #t
#f)
```

```scheme
#f
```

However, if a year is divisible by 400, it's still a leap year.
Let's choose 2000 this time around.
Using the previous code, we'd also get `#f` so we need to combine our three checks.
2000 is divisible by 4, 100, and 400. 1900 is divisible by 4, 100, but not 400.
Therefore we can use the `or` form to return `#t` if a year is divisible by 100 and 400 and `#f` otherwise.

```scheme
(and (zero? (modulo 2000 4))
(or (positive? (modulo year 100))
(zero? (modulo year 400))))
```

```scheme
(and #t
(or (positive? (modulo 2000 100))
(zero? (modulo year 400))))
```

```scheme
(and #t
(or #f
(zero? (modulo 2000 400))))
```

```scheme
(and #t
(or #f
#t))
```

```scheme
(and #t
#t)
```

```scheme
#t
```
4 changes: 4 additions & 0 deletions exercises/practice/leap/.approaches/operators/snippet.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
(define (leap-year? year)
(and (zero? (modulo year 4))
(or (positive? (modulo year 100))
(zero? (modulo year 400)))))

0 comments on commit 42256b9

Please sign in to comment.