Skip to content
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

Leap 48in24 approaches #1401

Merged
merged 33 commits into from
Jan 13, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
c4108ab
setting up the structure for leap approaches
michalporeba Jan 6, 2024
630a266
first draft of the introduction
michalporeba Jan 6, 2024
d4084e0
adding myself to the exercise contributors
michalporeba Jan 6, 2024
2b8ec47
fixing formatting
michalporeba Jan 6, 2024
616f6ec
snippets and headers as placeholders
michalporeba Jan 6, 2024
0a4fddc
improving the code with divides?
michalporeba Jan 6, 2024
5038fcb
more explicit parameters in divides?
michalporeba Jan 7, 2024
0e16a73
Include approach snippets in formatting
angelikatyborska Jan 8, 2024
0d19aa2
Update exercises/practice/leap/.approaches/introduction.md
michalporeba Jan 10, 2024
54a79e1
Update exercises/practice/leap/.approaches/introduction.md
michalporeba Jan 10, 2024
3c81c2c
Update exercises/practice/leap/.approaches/introduction.md
michalporeba Jan 10, 2024
09d55ca
Update exercises/practice/leap/.approaches/introduction.md
michalporeba Jan 10, 2024
085dfa3
Update exercises/practice/leap/.approaches/introduction.md
michalporeba Jan 10, 2024
6e85e4f
Update exercises/practice/leap/.approaches/introduction.md
michalporeba Jan 10, 2024
5eb7841
reformatting
michalporeba Jan 8, 2024
510c33c
boolean operators approach
michalporeba Jan 11, 2024
fc4a44c
rem by default, divides? as an option
michalporeba Jan 11, 2024
f98d44f
Update exercises/practice/leap/.approaches/introduction.md
michalporeba Jan 11, 2024
b9bd559
functions approach
michalporeba Jan 12, 2024
431da1d
control flow approaches
michalporeba Jan 13, 2024
deb494f
Update exercises/practice/leap/.approaches/introduction.md
michalporeba Jan 13, 2024
ff5bc09
Update exercises/practice/leap/.approaches/flow/content.md
michalporeba Jan 13, 2024
8db3e1e
Update exercises/practice/leap/.approaches/flow/content.md
michalporeba Jan 13, 2024
0754b52
Update exercises/practice/leap/.approaches/functions/content.md
michalporeba Jan 13, 2024
33e7fc6
Update exercises/practice/leap/.approaches/introduction.md
michalporeba Jan 13, 2024
a930e7f
Update exercises/practice/leap/.approaches/introduction.md
michalporeba Jan 13, 2024
06d05d2
Case on the tuple is the most idiomatic
michalporeba Jan 13, 2024
19a3bb6
typo
michalporeba Jan 13, 2024
6e527c7
clauses not functions
michalporeba Jan 13, 2024
16f7034
the number is a divisor
michalporeba Jan 13, 2024
5c8503c
Reformat code in code blocks
angelikatyborska Jan 13, 2024
a2172ae
Run through a grammar checker
angelikatyborska Jan 13, 2024
5a8d670
Format approach snippets
angelikatyborska Jan 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions exercises/practice/leap/.approaches/case/content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Using `case`
8 changes: 8 additions & 0 deletions exercises/practice/leap/.approaches/case/snippet.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
def leap_year?(year) do
case { rem(year, 400), rem(year, 100), rem(year, 4) } do
{ 0, _, _ } -> true
{ _, 0, _ } -> false
{ _, _, 0 } -> true
{ _, _, _ } -> false
michalporeba marked this conversation as resolved.
Show resolved Hide resolved
end
end
1 change: 1 addition & 0 deletions exercises/practice/leap/.approaches/cond/content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Using `cond`
8 changes: 8 additions & 0 deletions exercises/practice/leap/.approaches/cond/snippet.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
def leap_year?(year) do
cond do
divides?(year, 400) -> true
divides?(year, 100) -> false
divides?(year, 4) -> true
true -> false
michalporeba marked this conversation as resolved.
Show resolved Hide resolved
end
end
45 changes: 45 additions & 0 deletions exercises/practice/leap/.approaches/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"introduction": {
"authors": [
"michalporeba"
]
},
"approaches": [
{
"uuid": "be6d6c6e-8e19-4657-aad5-3382e7ec01db",
"slug": "operators",
"title": "Boolean Operators",
"blurb": "Use boolean operators to combine the checks.",
"authors": [
"michalporeba"
]
},
{
"uuid": "0267853e-9607-4b60-b2f9-e4a34f5316db",
"slug": "guards",
"title": "Function Guards",
"blurb": "Use function guards to control order of checks.",
"authors": [
"michalporeba"
]
},
{
"uuid": "428e3cee-309a-4c45-a6d4-3bff4eb41daa",
"slug": "cond",
"title": "Cond",
"blurb": "Use `cond` to control order of checks.",
"authors": [
"michalporeba"
]
},
{
"uuid": "129e6863-275b-4fa3-abfe-4cb07c97acae",
"slug": "case",
"title": "Case",
"blurb": "Use `case` and tuples to decide if the year is a leap one.",
"authors": [
"michalporeba"
]
}
]
}
1 change: 1 addition & 0 deletions exercises/practice/leap/.approaches/guards/content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Function Guards
4 changes: 4 additions & 0 deletions exercises/practice/leap/.approaches/guards/snippet.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
def leap_year?(year) when rem(year, 400) == 0, do: true
def leap_year?(year) when rem(year, 100) == 0, do: false
def leap_year?(year) when rem(year, 4) == 0, do: true
def leap_year?(_), do: false
96 changes: 96 additions & 0 deletions exercises/practice/leap/.approaches/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Introduction

Every fourth year is a leap year (with some exceptions), but let's consider this one condition first.

To solve the Leap problem, we must determine if a year is evenly divisible by a number or if a reminder of an integer division is zero.
Such operation in computing is called [modulo][modulo].

Unlike many languages, Elixir does not have [operators][operators] for either integer division or modulo.
Instead, it provides [`rem/2`][rem] guard and the [`Integer.mod/2`][mod] function.
michalporeba marked this conversation as resolved.
Show resolved Hide resolved

The two functions differ in how they work with negative numbers, but since, in this exercise,
all the numbers are non-negative, both could work, depending on the approach.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: I think there are no test cases for the year 0, so we could also say:

Suggested change
The two functions differ in how they work with negative numbers, but since, in this exercise,
all the numbers are non-negative, both could work, depending on the approach.
The two functions differ in how they work with negative numbers, but since, in this exercise,
all the input numbers are positive, both could work, depending on the approach.

(I'm specifying "input numbers" instead of just "numbers" to avoid people complaining that technically there is a zero in this exercise, in rem(year, n) == 0 😛 )

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used the "non-negative" here very intentionally. Positives start at 1, non-negative start at 0, so the zero is included. Probably a better name for the n parameter and for the description would be to say 'divisor' as this is technically what it is, and where the behaviour of the two functions differ.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alright, no problem, we can keep it


## General Solution
michalporeba marked this conversation as resolved.
Show resolved Hide resolved

To check if a year is divisible by `n`, we can do `rem(year, n) == 0`. We can define a function to make the intent clearer.

```elixir
defp divides?(number, divisor), do: rem(number, divisor) == 0
```
michalporeba marked this conversation as resolved.
Show resolved Hide resolved

Any approach to the problem will perform this check three times to check if a year is equally divisible by 4, 100 and 400.
What will differ is what Elixir features we will use to combine the checks.

## Approach: Boolean Operators

The full rules are as follows:
A year is a leap year if
* it is divisible by 4
* but not divisible by 100
* unless it is divisible by 400

We can use [boolean operators][boolean-operators] to combine the checks, for example, like so:

```elixir
divides?(year, 400) or (not(divides?(year, 100))) and divides?(year, 4)
```
In the [boolean operators appraoch][operators-approach] we discuss the details of the solution.
It includes variations of the operators and their precendence.
michalporeba marked this conversation as resolved.
Show resolved Hide resolved

## Approach: Function Guards
michalporeba marked this conversation as resolved.
Show resolved Hide resolved

Instead of using boolean operators, we can define multiple `leap_year?/1` functions with different guards.
michalporeba marked this conversation as resolved.
Show resolved Hide resolved
We can use the order of the definitions to ensure correct check.
michalporeba marked this conversation as resolved.
Show resolved Hide resolved

```elixir
def leap_year?(year) when rem(year, 400) == 0, do: true
def leap_year?(year) when rem(year, 100) == 0, do: false
def leap_year?(year) when rem(year, 4) == 0, do: true
def leap_year?(_), do: false
```

In the [functions with guards approach][guards-approach] we discuss why in this approach the `Integer.mod/2` function will not work.

## Approach: Using cond

Similarly to the functions with guards, the order of the checks can be done inside a function with `cond` expression.
michalporeba marked this conversation as resolved.
Show resolved Hide resolved

```elixir
cond do
divides?(year, 400) -> true
divides?(year, 100) -> false
divides?(year, 4) -> true
true -> false
end
```

We discuss this briefly in the [cond approach][cond-approach]

## Approach: Using case
michalporeba marked this conversation as resolved.
Show resolved Hide resolved

Using `case` is yet another way to check for a leap year.
This time, all the reminders are calculated and put into a tuple, and pattern matching is used to decide the outcome.

```elixir
case { rem(year, 400), rem(year, 100), rem(year, 4) } do
{ 0, _, _ } -> true
{ _, 0, _ } -> false
{ _, _, 0 } -> true
{ _, _, _ } -> false
michalporeba marked this conversation as resolved.
Show resolved Hide resolved
end
```
In the [case approach][case-approach] we discuss the pattern matchin in a case expression.


[modulo]: https://en.wikipedia.org/wiki/Modulo
[operators]: https://hexdocs.pm/elixir/1.16.0/operators.html
[rem]: https://hexdocs.pm/elixir/1.16.0/Kernel.html#rem/2
[mod]: https://hexdocs.pm/elixir/1.16.0/Integer.html#mod/2
[boolean-operators]: https://hexdocs.pm/elixir/1.11.4/operators.html#general-operators
michalporeba marked this conversation as resolved.
Show resolved Hide resolved
[operators-approach]: https://exercism.org/tracks/elixir/exercises/leap/approaches/operators
[guards-approach]: https://exercism.org/tracks/elixir/exercises/leap/approaches/guards
[cond-approach]: https://exercism.org/tracks/elixir/exercises/leap/approaches/cond
[case-approach]: https://exercism.org/tracks/elixir/exercises/leap/approaches/case


1 change: 1 addition & 0 deletions exercises/practice/leap/.approaches/operators/content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Boolean Operators
5 changes: 5 additions & 0 deletions exercises/practice/leap/.approaches/operators/snippet.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
def leap_year?(year) do
divides?(year, 400)
or (not(divides?(year, 100)))
and divides?(year, 4)
end
1 change: 1 addition & 0 deletions exercises/practice/leap/.meta/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"korbin",
"kytrinyx",
"lpil",
"michalporeba",
"neenjaw",
"parkerl",
"sotojuan",
Expand Down