Skip to content

Commit

Permalink
functions approach
Browse files Browse the repository at this point in the history
  • Loading branch information
michalporeba committed Jan 12, 2024
1 parent f98d44f commit b9bd559
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 7 deletions.
6 changes: 3 additions & 3 deletions exercises/practice/leap/.approaches/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
},
{
"uuid": "0267853e-9607-4b60-b2f9-e4a34f5316db",
"slug": "guards",
"title": "Function Guards",
"blurb": "Use function guards to control order of checks.",
"slug": "functions",
"title": "Multiple clause functions",
"blurb": "Use multiple clause function to control the order of checks.",
"authors": [
"michalporeba"
]
Expand Down
50 changes: 50 additions & 0 deletions exercises/practice/leap/.approaches/functions/content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Multiple clause functions

```elixir
defmodule Year do
@spec leap_year?(non_neg_integer) :: boolean
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
end
```

In Elixir, functions can have multiple clauses.
Which one will be executed depends on parameter matching and guards.
When a function with multiple clauses is invoked, the parameters are compared to the definitions in the order in which they were defined, and only the first one matching will be invoked.

While in the [operators approach][operators-approach], it was possible to reorder expressions as long as the suitable boolean operators were used, in this approach, there is only one correct order of definitions.

In our case, the three guards in the function clauses are as follows:

```elixir
when rem(year, 400) == 0
when rem(year, 100) == 0
when rem(year, 4) == 0
```

But because of the order they are evaluated in, they are equivalent to:

```elixir
when rem(year, 400) == 0
when rem(year, 100) == 0 and not rem(year, 400) == 0
when rem(year, 4) == 0 and not rem(year, 100) == 0 and not rem(year, 400) = 0
```

The final clause, `def leap_year?(_), do: false`, returns false if previous clauses are not a match.

## Guards

The [guards][guards] are part of the pattern-matching mechanism.
They allow for more complex checks of values.
However, because of when they are executed to allow the compiler to perform necessary optimization,
only a minimal subset of operations are permitted.
`Kernel.rem/2` is on this limited list, and `Integer.mod/2` is not.
This is why, in this approach, only the first one will work, and the latter will not.

In this approach, the boolean operators matter too. Only the strict ones, `not`, `and`, `or` are allowed.
The relaxed `!`, `&&`, `||` will fail to compile.

[operators-approach]: https://exercism.org/tracks/elixir/exercises/leap/approaches/operators
[guards]: https://hexdocs.pm/elixir/main/patterns-and-guards.html#guards
Original file line number Diff line number Diff line change
@@ -1,4 +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
def leap_year?(_), do: false
1 change: 0 additions & 1 deletion exercises/practice/leap/.approaches/guards/content.md

This file was deleted.

4 changes: 2 additions & 2 deletions exercises/practice/leap/.approaches/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ 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.
In the [multiple clause functions approach][functions-approach] we discuss why in this approach the `Integer.mod/2` function will not work.

## Approach: using cond

Expand Down Expand Up @@ -85,7 +85,7 @@ In the [case approach][case-approach] we discuss the pattern matchin in a case e
[mod]: https://hexdocs.pm/elixir/Integer.html#mod/2
[boolean-operators]: https://hexdocs.pm/elixir/operators.html#general-operators
[operators-approach]: https://exercism.org/tracks/elixir/exercises/leap/approaches/operators
[guards-approach]: https://exercism.org/tracks/elixir/exercises/leap/approaches/guards
[functions-approach]: https://exercism.org/tracks/elixir/exercises/leap/approaches/functions
[cond-approach]: https://exercism.org/tracks/elixir/exercises/leap/approaches/cond
[case-approach]: https://exercism.org/tracks/elixir/exercises/leap/approaches/case

Expand Down

0 comments on commit b9bd559

Please sign in to comment.