Skip to content

Commit

Permalink
rem by default, divides? as an option
Browse files Browse the repository at this point in the history
  • Loading branch information
michalporeba committed Jan 11, 2024
1 parent 510c33c commit fc4a44c
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 20 deletions.
14 changes: 5 additions & 9 deletions exercises/practice/leap/.approaches/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,6 @@ all the numbers are non-negative, both could work, depending on the approach.
## General solution

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
```

Any approach to the problem will perform this check three times to see if a year is equally divisible by 4, 100 and 400.
What will differ between approaches is what Elixir features we will use to combine the checks.
Expand All @@ -34,7 +29,7 @@ A year is a leap year if
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)
rem(year, 5) == 0 and not rem(year, 100) == 0 or rem(year, 400) == 0
```
In the [boolean operators appraoch][operators-approach] we discuss the details of the solution.
It includes variations of the operators and their precendence.
Expand All @@ -59,9 +54,9 @@ Similarly to the multiple clause function approach, we can also use a `cond` exp

```elixir
cond do
divides?(year, 400) -> true
divides?(year, 100) -> false
divides?(year, 4) -> true
rem(year, 400) == 0 -> true
rem(year, 100) == 0 -> false
rem(year, 4) == 0 -> true
true -> false
end
```
Expand All @@ -81,6 +76,7 @@ case { rem(year, 400), rem(year, 100), rem(year, 4) } do
{ _, _, _ } -> false
end
```

In the [case approach][case-approach] we discuss the pattern matchin in a case expression.


Expand Down
22 changes: 14 additions & 8 deletions exercises/practice/leap/.approaches/operators/content.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@

```elixir
defmodule Year do
@spec divides?(non_neg_integer, non_neg_integer) :: boolean
defp divides?(number, divisor), do: rem(number, divisor) == 0

@spec leap_year?(non_neg_integer) :: boolean
def leap_year?(year) do
divides?(year, 4) and not divides?(year, 100) or divides?(year, 400)
rem(year, 4) == 0 and not rem(year, 100) == 0 or rem(year, 400) == 0
end
end
```
Expand All @@ -26,7 +23,6 @@ The expression `left or right` can be true if either `left` or `right` is *true*
If `left` is *true*, `right` will not be evaluated. The result will be *true*.
However, if `left` is *false*, `right` has to be evaluated to determine the outcome.


## Precedence of operators

Another thing to consider when using Boolean operators is their precedence.
Expand All @@ -52,7 +48,12 @@ Elixir offers two sets of Boolean operators: strict and relaxed.
The strict versions `not`, `and`, `or` require the first (left) argument to be of [boolean type][hexdocs-booleans].
The relaxed versions `!`, `&&`, `||` require the first argument to be only [truthy or falsy][hexdocs-truthy].

In the case of this exercise, both types will work equally well.
In the case of this exercise, both types will work equally well, so the solution could be:
```elixir
def leap_year?(year) do
rem(year, 4) == 0 && !(rem(year, 100) == 0) || rem(year, 400) == 0
end
```

## Being explicit

Expand All @@ -62,19 +63,24 @@ def leap_year?(year) do
rem(year, 4) == 0 and not rem(year, 100) == 0 or rem(year, 400) == 0
end
```

Some prefer this form, as it is very direct. We can see what is happening.
We are explicitly checking the reminder, comparing it to zero.

```elixir
defp divides?(number, divisor), do: rem(number, divisor) == 0

def leap_year?(year) do
divides?(year, 4) and not divides?(year, 100) or divides?(year, 400)
end
```
Other might prefer the above form, which requires defining the `devides?` function.

Other might prefer the above form, which requires defining the `devides?` function or something similar.
By doing so, we can be explicit about the *intent*.
We want to check if a year can be equally divided into a number.

Yet another approach might be use variables to capture the results of individual checks.
Yet another approach might be to use variables to capture the results of individual checks and provided the extra meaning.
This approach also shortens the check so the Boolean operators and relationships between them are more prominent.

```elixir
def leap_year?(year) do
Expand Down
6 changes: 3 additions & 3 deletions exercises/practice/leap/.approaches/operators/snippet.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
def leap_year?(year) do
divides?(year, 4) and
not divides?(year, 100) or
divides?(year, 400)
rem(year, 4) == 0 and
not rem(year, 100) == 0 or
rem(year, 400) == 0
end

0 comments on commit fc4a44c

Please sign in to comment.