diff --git a/exercises/practice/leap/.approaches/clauses/content.md b/exercises/practice/leap/.approaches/clauses/content.md index f00ff3261..03601d59f 100644 --- a/exercises/practice/leap/.approaches/clauses/content.md +++ b/exercises/practice/leap/.approaches/clauses/content.md @@ -10,17 +10,17 @@ defmodule Year do 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. +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. +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: +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, 100) == 0 when rem(year, 4) == 0 ``` @@ -28,7 +28,7 @@ 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, 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 ``` @@ -36,15 +36,15 @@ The final clause, `def leap_year?(_), do: false`, returns false if previous clau ## Guards -The [guards][hexdocs-guards] are part of the pattern-matching mechanism. -They allow for more complex checks of values. +The [guards][hexdocs-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 is 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. +only a minimal subset of operations is 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. +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 [hexdocs-guards]: https://hexdocs.pm/elixir/main/patterns-and-guards.html#guards diff --git a/exercises/practice/leap/.approaches/flow/content.md b/exercises/practice/leap/.approaches/flow/content.md index af8c7fca0..02e9895bd 100644 --- a/exercises/practice/leap/.approaches/flow/content.md +++ b/exercises/practice/leap/.approaches/flow/content.md @@ -6,7 +6,7 @@ defmodule Year do def leap_year?(year) do if rem(year, 100) == 0 do rem(year, 400) == 0 - else + else rem(year, 4) == 0 end end @@ -15,25 +15,26 @@ end ## If -Elixir provides four [control flow structures][hexdocs-structures]: `case`, `cond`, `if`, and `unless`. -The `if` and `unless` allow to evaluate only one condition. -Unlike in many other languages, there is no `else if` option in Elixir. +Elixir provides four [control flow structures][hexdocs-structures]: `case`, `cond`, `if`, and `unless`. -However, in this case, it is not necessary. We can use `if` once to check if the year is divisible by 100. -If it is, then whether it is a leap year or not depends on if it is divisible by 400. -If it is not, then whether it is a leap year or not depends on if it is divisible by 4. +The `if` and `unless` allow to evaluate only one condition. +Unlike in many other languages, there is no `else if` option in Elixir. + +However, in this case, it is not necessary. We can use `if` once to check if the year is divisible by 100. +If it is, then whether it is a leap year or not depends on if it is divisible by 400. +If it is not, then whether it is a leap year or not depends on if it is divisible by 4. ```elixir def leap_year?(year) do if rem(year, 100) == 0 do rem(year, 400) == 0 - else + else rem(year, 4) == 0 end end ``` -## Cond +## Cond Another option is `cond` which allows for evaluating multiple conditions, similar to `else if` in other languages. @@ -48,12 +49,12 @@ def leap_year?(year) do end ``` -Similarly to the [multiple clause function approach][clause-approach], the order here matters. -The conditions are evaluated in order, and the first that is not `nil` or `false` leads to the result. +Similarly to the [multiple clause function approach][clause-approach], the order here matters. +The conditions are evaluated in order, and the first that is not `nil` or `false` leads to the result. ## Case -`case` allows to compare a value to multiple patterns, but can also replicate what `if` offers. +`case` allows to compare a value to multiple patterns, but can also replicate what `if` offers. ```elixir def leap_year?(year) do @@ -64,7 +65,7 @@ def leap_year?(year) do end ``` -It also supports [guards][hexdocs-guards], offering another way to solve the problem. +It also supports [guards][hexdocs-guards], offering another way to solve the problem. ```elixir def leap_year?(year) do @@ -74,13 +75,13 @@ def leap_year?(year) do _ when rem(year, 4) == 0 -> true _ -> false end -end +end ``` -The `case` can be very flexible, so many variations are possible. -Using it with pattern matching on a tuple is considered **the most idiomatic**. -In this case, a tuple is created with all the checks. -Then, pattern matching to tuples is performed. +The `case` can be very flexible, so many variations are possible. +Using it with pattern matching on a tuple is considered **the most idiomatic**. +In this case, a tuple is created with all the checks. +Then, pattern matching to tuples is performed. ```elixir def leap_year?(year) do diff --git a/exercises/practice/leap/.approaches/introduction.md b/exercises/practice/leap/.approaches/introduction.md index 1e56f8b45..be5e0faaa 100644 --- a/exercises/practice/leap/.approaches/introduction.md +++ b/exercises/practice/leap/.approaches/introduction.md @@ -1,11 +1,11 @@ # Introduction -Every fourth year is a leap year (with some exceptions), but let's consider this one condition first. +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]. +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. +Unlike many languages, Elixir does not have [operators][operators] for either integer division or modulo. Instead, it provides the [`Kernel.rem/2`][rem] and the [`Integer.mod/2`][mod] functions. The two functions differ in how they work with negative divisors, but since, in this exercise, @@ -13,20 +13,20 @@ all the divisors are non-negative, both could work, depending on the approach yo ## General solution -To check if a year is divisible by `n`, we can do `rem(year, n) == 0`. +To check if a year is divisible by `n`, we can do `rem(year, n) == 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. +What will differ between approaches 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 +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: +We can use [boolean operators][boolean-operators] to combine the checks, for example, like so: ```elixir rem(year, 5) == 0 and not rem(year, 100) == 0 or rem(year, 400) == 0 @@ -36,7 +36,7 @@ It includes variations of the operators and their precedence. ## Approach: multiple clause function -Instead of using boolean operators, we can define multiple `leap_year?/1` function clauses with different guards. +Instead of using boolean operators, we can define multiple `leap_year?/1` function clauses with different guards. ```elixir def leap_year?(year) when rem(year, 400) == 0, do: true @@ -45,17 +45,17 @@ def leap_year?(year) when rem(year, 4) == 0, do: true def leap_year?(_), do: false ``` -In the [multiple clause function approach][clause-approach] we discuss why in this approach the `Integer.mod/2` function will not work. +In the [multiple clause function approach][clause-approach] we discuss why in this approach the `Integer.mod/2` function will not work. ## Approach: control flow structures In addition to the above two approaches, control flow structures offer a number of solutions. -Here are two examples using `if` and `case`. +Here are two examples using `if` and `case`. ```elixir if rem(year, 100) == 0 do rem(year, 400) == 0 -else +else rem(year, 4) == 0 end ``` @@ -78,6 +78,6 @@ We discuss these and other solutions depending on various control flow structure [boolean-operators]: https://hexdocs.pm/elixir/operators.html#general-operators [operators-approach]: https://exercism.org/tracks/elixir/exercises/leap/approaches/operators [clause-approach]: https://exercism.org/tracks/elixir/exercises/leap/approaches/clauses -[flow-approach]: https://exercism.org/tracks/elixir/exercises/leap/approaches/cond +[flow-approach]: https://exercism.org/tracks/elixir/exercises/leap/approaches/flow diff --git a/exercises/practice/leap/.approaches/operators/content.md b/exercises/practice/leap/.approaches/operators/content.md index cef649543..1b43836f6 100644 --- a/exercises/practice/leap/.approaches/operators/content.md +++ b/exercises/practice/leap/.approaches/operators/content.md @@ -11,40 +11,40 @@ end ## Short-circuiting -At the core of this approach, three checks are returning three boolean values. +At the core of this approach, three checks are returning three boolean values. We can use [Boolean logic](https://en.wikipedia.org/wiki/Boolean_algebra) to combine the results. -When using this approach, it is essential to consider short-circuiting of boolean operators. -The expression `left and right` can be only true if both `left` and `right` are *true*. -If `left` is *false*, `right` will not be evaluated. The result will be *false*. +When using this approach, it is essential to consider short-circuiting of boolean operators. +The expression `left and right` can be only true if both `left` and `right` are *true*. +If `left` is *false*, `right` will not be evaluated. The result will be *false*. However, if `left` is *true*, `right` has to be evaluated to determin the outcome. -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*. +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. +Another thing to consider when using Boolean operators is their precedence. ```elixir true or false and false ``` -The above evaluates to *true* because in Elixir `and` has higher precedence than `or`. +The above evaluates to *true* because in Elixir `and` has higher precedence than `or`. The above expression is equivalent to: ```elixir true or (false and false) ``` -If `or` should be evaluated first, we must use parenthesis. +If `or` should be evaluated first, we must use parenthesis. ```elixir (true or false) and false ``` -which equals to *false*. +which equals to *false*. -The `not` operator is evaluated before `and` and `or`. +The `not` operator is evaluated before `and` and `or`. ## Strict or relaxed? -Elixir offers two sets of Boolean operators: strict and relaxed. +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]. @@ -64,8 +64,8 @@ def leap_year?(year) do 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. +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 @@ -75,12 +75,12 @@ def leap_year?(year) do end ``` -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. +Others 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 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. +Yet another approach might be to use variables to capture the results of individual checks and provide 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 @@ -91,7 +91,7 @@ def leap_year?(year) do end ``` -All versions of the code will work. Which one to choose is often a personal or sometimes a team preference. What reads best for you? What will make most sense to you when you look at the code again? +All versions of the code will work. Which one to choose is often a personal or sometimes a team preference. What reads best for you? What will make most sense to you when you look at the code again? [hexdocs-booleans]: https://hexdocs.pm/elixir/basic-types.html#booleans-and-nil [hexdocs-truthy]: https://hexdocs.pm/elixir/Kernel.html#module-truthy-and-falsy-values