|
| 1 | +# Step By Step |
| 2 | + |
| 3 | +```elixir |
| 4 | +defmodule Raindrops do |
| 5 | + @spec convert(pos_integer) :: String.t() |
| 6 | + def convert(number) do |
| 7 | + pling = if rem(number, 3) == 0, do: "Pling", else: "" |
| 8 | + plang = if rem(number, 5) == 0, do: "Plang", else: "" |
| 9 | + plong = if rem(number, 7) == 0, do: "Plong", else: "" |
| 10 | + sound = pling <> plang <> plong |
| 11 | + |
| 12 | + if sound == "" do |
| 13 | + Integer.to_string(number) |
| 14 | + else |
| 15 | + sound |
| 16 | + end |
| 17 | + end |
| 18 | +end |
| 19 | +``` |
| 20 | + |
| 21 | +In this approach, we test each condition only once, similar to using the `case` on a tuple in the [pattern matching approach][pattern-matching-approach]. |
| 22 | +However, this time, if a condition is true, we capture the sound component, and if it is not true, we capture the sound as an empty string. |
| 23 | + |
| 24 | +Once this is done, we can concatenate all three conditions to get the full sound. |
| 25 | +Finally, if the `sound` is empty, we can return the number or, alternatively, the calculated `sound`. |
| 26 | + |
| 27 | +## Functions |
| 28 | + |
| 29 | +We can create private functions to test for component sounds. |
| 30 | + |
| 31 | +```elixir |
| 32 | +defp pling(n) when rem(n, 3) == 0, do: "Pling" |
| 33 | +defp pling(_), do: "" |
| 34 | +defp plang(n) when rem(n, 5) == 0, do: "Plang" |
| 35 | +defp plang(_), do: "" |
| 36 | +defp plong(n) when rem(n, 7) == 0, do: "Plong" |
| 37 | +defp plong(_), do: "" |
| 38 | +defp sound(sound, number) when sound == "", do: Integer.to_string(number) |
| 39 | +defp sound(sound, _number), do: sound |
| 40 | +``` |
| 41 | + |
| 42 | +Now the solution can look like this: |
| 43 | +```elixir |
| 44 | +def convert(number) do |
| 45 | + sound(pling(number) <> plang(number) <> plong(number), number) |
| 46 | +end |
| 47 | +``` |
| 48 | + |
| 49 | +## The pipe operator |
| 50 | + |
| 51 | +With a slightly different design of the functions we can use the pipe operator to have a very clean-looking code |
| 52 | + |
| 53 | +```elixir |
| 54 | +@spec convert(pos_integer) :: String.t() |
| 55 | +def convert(number) do |
| 56 | + {"", number} |
| 57 | + |> pling |
| 58 | + |> plang |
| 59 | + |> plong |
| 60 | + |> sound |
| 61 | +end |
| 62 | +``` |
| 63 | +At least in the `convert` functions. The `pling`, `plang`, `plong` become a bit more complex: |
| 64 | +```elixir |
| 65 | +defp pling({ s, n }) when rem(n, 3) == 0, do: { s <> "Pling", n } |
| 66 | +defp pling({ s, n }), do: { s, n } |
| 67 | +defp plang({ s, n }) when rem(n, 5) == 0, do: { s <> "Plang", n } |
| 68 | +defp plang({ s, n }), do: { s, n } |
| 69 | +defp plong({ s, n }) when rem(n, 7) == 0, do: { s <> "Plong", n } |
| 70 | +defp plong({ s, n }), do: { s, n } |
| 71 | +defp sound({ s, n }) when s == "" , do: n |> Integer.to_string |
| 72 | +defp sound({ s, _ }), do: s |
| 73 | +``` |
| 74 | + |
| 75 | +All the examples above, at their core, represent the same approach of doing the check step by step. |
| 76 | + |
| 77 | +[pattern-matching-approach]: https://exercism.org/tracks/elixir/exercises/raindrops/approaches/pattern-matching |
0 commit comments