From 51b120b730e051790af72da651ac6fd935f2e74c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Por=C4=99ba?= Date: Fri, 26 Jan 2024 19:23:21 +0000 Subject: [PATCH 1/6] patterns --- .../raindrops/.approaches/config.json | 11 ++++++++++ .../.approaches/pattern-matching/content.md | 2 ++ .../pattern-matching/introduction.md | 20 +++++++++++++++++++ .../.approaches/pattern-matching/snippet.txt | 0 4 files changed, 33 insertions(+) create mode 100644 exercises/practice/raindrops/.approaches/config.json create mode 100644 exercises/practice/raindrops/.approaches/pattern-matching/content.md create mode 100644 exercises/practice/raindrops/.approaches/pattern-matching/introduction.md create mode 100644 exercises/practice/raindrops/.approaches/pattern-matching/snippet.txt diff --git a/exercises/practice/raindrops/.approaches/config.json b/exercises/practice/raindrops/.approaches/config.json new file mode 100644 index 0000000000..717ad940dd --- /dev/null +++ b/exercises/practice/raindrops/.approaches/config.json @@ -0,0 +1,11 @@ +{ + "approaches": [ + { + "uuid": "fbfbabb4-f4e6-4329-b2f5-a93e3199b809", + "slug": "pattern-matching", + "title": "Pattern Matching", + "blurb": "", + "authors": [] + } + ] +} diff --git a/exercises/practice/raindrops/.approaches/pattern-matching/content.md b/exercises/practice/raindrops/.approaches/pattern-matching/content.md new file mode 100644 index 0000000000..12e09a1a51 --- /dev/null +++ b/exercises/practice/raindrops/.approaches/pattern-matching/content.md @@ -0,0 +1,2 @@ +# Pattern Matching + diff --git a/exercises/practice/raindrops/.approaches/pattern-matching/introduction.md b/exercises/practice/raindrops/.approaches/pattern-matching/introduction.md new file mode 100644 index 0000000000..cadc242ae0 --- /dev/null +++ b/exercises/practice/raindrops/.approaches/pattern-matching/introduction.md @@ -0,0 +1,20 @@ +# Introduction + +## Pattern matching + +We can be very explicit with the solution and explicitly match all the possible patterns. + +```elixir +def convert(number) do + case {rem(number, 3), rem(number, 5), rem(number, 7)} do + {0, 0, 0} -> "PlingPlangPlong" + {0, 0, _} -> "PlingPlang" + {0, _, 0} -> "PlingPlong" + {_, 0, 0} -> "PlangPlong" + {0, _, _} -> "Pling" + {_, 0, _} -> "Plang" + {_, _, 0} -> "Plong" + _ -> Integer.to_string(number) + end +end +``` \ No newline at end of file diff --git a/exercises/practice/raindrops/.approaches/pattern-matching/snippet.txt b/exercises/practice/raindrops/.approaches/pattern-matching/snippet.txt new file mode 100644 index 0000000000..e69de29bb2 From 4bc625374296a1054372dce6aabf2bde367eb461 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Por=C4=99ba?= Date: Fri, 26 Jan 2024 19:51:28 +0000 Subject: [PATCH 2/6] pattern approach --- .../raindrops/.approaches/introduction.md | 25 +++++++ .../.approaches/pattern-matching/content.md | 73 +++++++++++++++++++ .../pattern-matching/introduction.md | 20 ----- .../.approaches/pattern-matching/snippet.txt | 12 +++ 4 files changed, 110 insertions(+), 20 deletions(-) create mode 100644 exercises/practice/raindrops/.approaches/introduction.md delete mode 100644 exercises/practice/raindrops/.approaches/pattern-matching/introduction.md diff --git a/exercises/practice/raindrops/.approaches/introduction.md b/exercises/practice/raindrops/.approaches/introduction.md new file mode 100644 index 0000000000..6af4e3e626 --- /dev/null +++ b/exercises/practice/raindrops/.approaches/introduction.md @@ -0,0 +1,25 @@ +# Introduction + +## Pattern matching + +The output of the `convert` method depends on three conditions. +We can be very explicit with the solution and match all the possible patterns. + +```elixir +def convert(number) do + case {rem(number, 3), rem(number, 5), rem(number, 7)} do + {0, 0, 0} -> "PlingPlangPlong" + {0, 0, _} -> "PlingPlang" + {0, _, 0} -> "PlingPlong" + {_, 0, 0} -> "PlangPlong" + {0, _, _} -> "Pling" + {_, 0, _} -> "Plang" + {_, _, 0} -> "Plong" + _ -> Integer.to_string(number) + end +end +``` + +We can use a few Elixir features to do more or less the same and we explore them in the [pattern matching approach][pattern-matching-approach]. + +[pattern-matching-approach]: https://exercism.org/tracks/elixir/exercises/raindrops/approaches/pattern-matching \ No newline at end of file diff --git a/exercises/practice/raindrops/.approaches/pattern-matching/content.md b/exercises/practice/raindrops/.approaches/pattern-matching/content.md index 12e09a1a51..dfb0866142 100644 --- a/exercises/practice/raindrops/.approaches/pattern-matching/content.md +++ b/exercises/practice/raindrops/.approaches/pattern-matching/content.md @@ -1,2 +1,75 @@ # Pattern Matching +```elixir +defmodule Raindrops do + @spec convert(pos_integer) :: String.t() + def convert(number) do + case {rem(number, 3), rem(number, 5), rem(number, 7)} do + {0, 0, 0} -> "PlingPlangPlong" + {0, 0, _} -> "PlingPlang" + {0, _, 0} -> "PlingPlong" + {_, 0, 0} -> "PlangPlong" + {0, _, _} -> "Pling" + {_, 0, _} -> "Plang" + {_, _, 0} -> "Plong" + _ -> Integer.to_string(number) + end + end +end +``` + +## Case +The case allows us to evaluate if a number is divisible by 3, 5 and 7 once, and then match the results to various combinations of the possible outcomes. +The advantage of using a `case` on a tuple, like in the example above, is that the `rem` functions are executed only once. + +## Cond +We can use `cond do`, too. +However, first, let's look at the maths to make the solution more compact. + +A number is divisible by `a`, `b`, and `c` only when it is divisible by `a*b*c`. +So, instead of +```elixir +rem(number, 3) == 0 and rem(number, 5) == 0 and rem(number, 7) == 0 +``` +we can write +```elixir +rem(number, 3*5*7) == 0 +``` + +Now, let's look at this pattern matching with `cond`. + +```elixir +def convert(number) do + cond do + rem(number, 3*5*7) == 0 -> "PlingPlangPlong" + rem(number, 3*5) == 0 -> "PlingPlang" + rem(number, 3*7) == 0 -> "PlingPlong" + rem(number, 5*7) == 0 -> "PlangPlong" + rem(number, 3) == 0 -> "Pling" + rem(number, 5) == 0 -> "Plang" + rem(number, 7) == 0 -> "Plong" + true -> Integer.to_string(number) + end +end +``` + +## Multiple-clause functions +We can do something very similar by using guards in multi-clause functions. +We use different feautre of the language, but at its core, the approach is the same. + +```elixir +defmodule Raindrops do + @spec convert(pos_integer) :: String.t() + def convert(number) when rem(number, 3*5*7) == 0, do: "PlingPlangPlong" + def convert(number) when rem(number, 3*5) == 0, do: "PlingPlang" + def convert(number) when rem(number, 3*7) == 0, do: "PlingPlong" + def convert(number) when rem(number, 5*7) == 0, do: "PlangPlong" + def convert(number) when rem(number, 3) == 0, do: "Pling" + def convert(number) when rem(number, 5) == 0, do: "Plang" + def convert(number) when rem(number, 7) == 0, do: "Plong" + def convert(number), do: Integer.to_string(number) +end +``` + +We can use different features of the language, but at its core, the approach is the same. +We check a set of conditions that leads us to the exact answer. \ No newline at end of file diff --git a/exercises/practice/raindrops/.approaches/pattern-matching/introduction.md b/exercises/practice/raindrops/.approaches/pattern-matching/introduction.md deleted file mode 100644 index cadc242ae0..0000000000 --- a/exercises/practice/raindrops/.approaches/pattern-matching/introduction.md +++ /dev/null @@ -1,20 +0,0 @@ -# Introduction - -## Pattern matching - -We can be very explicit with the solution and explicitly match all the possible patterns. - -```elixir -def convert(number) do - case {rem(number, 3), rem(number, 5), rem(number, 7)} do - {0, 0, 0} -> "PlingPlangPlong" - {0, 0, _} -> "PlingPlang" - {0, _, 0} -> "PlingPlong" - {_, 0, 0} -> "PlangPlong" - {0, _, _} -> "Pling" - {_, 0, _} -> "Plang" - {_, _, 0} -> "Plong" - _ -> Integer.to_string(number) - end -end -``` \ No newline at end of file diff --git a/exercises/practice/raindrops/.approaches/pattern-matching/snippet.txt b/exercises/practice/raindrops/.approaches/pattern-matching/snippet.txt index e69de29bb2..1fea8073e3 100644 --- a/exercises/practice/raindrops/.approaches/pattern-matching/snippet.txt +++ b/exercises/practice/raindrops/.approaches/pattern-matching/snippet.txt @@ -0,0 +1,12 @@ +def convert(number) do + case {rem(number, 3), rem(number, 5), rem(number, 7)} do + {0, 0, 0} -> "PlingPlangPlong" + {0, 0, _} -> "PlingPlang" + {0, _, 0} -> "PlingPlong" + {_, 0, 0} -> "PlangPlong" + {0, _, _} -> "Pling" + {_, 0, _} -> "Plang" + {_, _, 0} -> "Plong" + _ -> Integer.to_string(number) + end +end From d0685c95422de30effc644142484052c30a739fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Por=C4=99ba?= Date: Fri, 26 Jan 2024 20:29:23 +0000 Subject: [PATCH 3/6] step-by-step --- .../raindrops/.approaches/config.json | 7 ++ .../raindrops/.approaches/introduction.md | 26 ++++++- .../.approaches/step-by-step/content.md | 77 +++++++++++++++++++ .../.approaches/step-by-step/snippet.txt | 12 +++ 4 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 exercises/practice/raindrops/.approaches/step-by-step/content.md create mode 100644 exercises/practice/raindrops/.approaches/step-by-step/snippet.txt diff --git a/exercises/practice/raindrops/.approaches/config.json b/exercises/practice/raindrops/.approaches/config.json index 717ad940dd..460c5f4e6e 100644 --- a/exercises/practice/raindrops/.approaches/config.json +++ b/exercises/practice/raindrops/.approaches/config.json @@ -6,6 +6,13 @@ "title": "Pattern Matching", "blurb": "", "authors": [] + }, + { + "uuid": "925ccb59-3414-472b-9054-1cdfc5e44fad", + "slug": "step-by-step", + "title": "Step By Step", + "blurb": "", + "authors": [] } ] } diff --git a/exercises/practice/raindrops/.approaches/introduction.md b/exercises/practice/raindrops/.approaches/introduction.md index 6af4e3e626..38fadf4c8a 100644 --- a/exercises/practice/raindrops/.approaches/introduction.md +++ b/exercises/practice/raindrops/.approaches/introduction.md @@ -22,4 +22,28 @@ end We can use a few Elixir features to do more or less the same and we explore them in the [pattern matching approach][pattern-matching-approach]. -[pattern-matching-approach]: https://exercism.org/tracks/elixir/exercises/raindrops/approaches/pattern-matching \ No newline at end of file +## Step by step + +An alternative approach is to consider each condition one at a time. +At each step we return either a sound (i.e. "Pling", "Plang", or "Plong"), or an empty string. +We can then concatenate the strings together. + +```elixir +def convert(number) do + pling = if rem(number, 3) == 0, do: "Pling", else: "" + plang = if rem(number, 5) == 0, do: "Plang", else: "" + plong = if rem(number, 7) == 0, do: "Plong", else: "" + result = pling <> plang <> plong + + if result == "" do + Integer.to_string(number) + else + result + end +end +``` + +Let's have a look at a few variations of this [step by step approach][step-by-step-approach]. + +[pattern-matching-approach]: https://exercism.org/tracks/elixir/exercises/raindrops/approaches/pattern-matching +[step-by-step-approach]: https://exercism.org/tracks/elixir/exercises/raindrops/approaches/step-by-step \ No newline at end of file diff --git a/exercises/practice/raindrops/.approaches/step-by-step/content.md b/exercises/practice/raindrops/.approaches/step-by-step/content.md new file mode 100644 index 0000000000..0bbdf724cd --- /dev/null +++ b/exercises/practice/raindrops/.approaches/step-by-step/content.md @@ -0,0 +1,77 @@ +# Step By Step + +```elixir +defmodule Raindrops do + @spec convert(pos_integer) :: String.t() + def convert(number) do + pling = if rem(number, 3) == 0, do: "Pling", else: "" + plang = if rem(number, 5) == 0, do: "Plang", else: "" + plong = if rem(number, 7) == 0, do: "Plong", else: "" + sound = pling <> plang <> plong + + if sound == "" do + Integer.to_string(number) + else + sound + end + end +end +``` + +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]. +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. + +Once this is done, we can concatenate all three conditions to get the full sound. +Finally, if the `sound` is empty, we can return the number or, alternatively, the calculated `sound`. + +## Functions + +We can create private functions to test for component sounds. + +```elixir +defp pling(n) when rem(n, 3) == 0, do: "Pling" +defp pling(_), do: "" +defp plang(n) when rem(n, 5) == 0, do: "Plang" +defp plang(_), do: "" +defp plong(n) when rem(n, 7) == 0, do: "Plong" +defp plong(_), do: "" +defp sound(sound, number) when sound == "", do: Integer.to_string(number) +defp sound(sound, _number), do: sound +``` + +Now the solution can look like this: +```elixir +def convert(number) do + sound(pling(number) <> plang(number) <> plong(number), number) +end +``` + +## The pipe operator + +With a slightly different design of the functions we can use the pipe operator to have a very clean-looking code + +```elixir +@spec convert(pos_integer) :: String.t() +def convert(number) do + {"", number} + |> pling + |> plang + |> plong + |> sound +end +``` +At least in the `convert` functions. The `pling`, `plang`, `plong` become a bit more complex: +```elixir +defp pling({ s, n }) when rem(n, 3) == 0, do: { s <> "Pling", n } +defp pling({ s, n }), do: { s, n } +defp plang({ s, n }) when rem(n, 5) == 0, do: { s <> "Plang", n } +defp plang({ s, n }), do: { s, n } +defp plong({ s, n }) when rem(n, 7) == 0, do: { s <> "Plong", n } +defp plong({ s, n }), do: { s, n } +defp sound({ s, n }) when s == "" , do: n |> Integer.to_string +defp sound({ s, _ }), do: s +``` + +All the examples above, at their core, represent the same approach of doing the check step by step. + +[pattern-matching-approach]: https://exercism.org/tracks/elixir/exercises/raindrops/approaches/pattern-matching \ No newline at end of file diff --git a/exercises/practice/raindrops/.approaches/step-by-step/snippet.txt b/exercises/practice/raindrops/.approaches/step-by-step/snippet.txt new file mode 100644 index 0000000000..7b9903f613 --- /dev/null +++ b/exercises/practice/raindrops/.approaches/step-by-step/snippet.txt @@ -0,0 +1,12 @@ +def convert(number) do + pling = if rem(number, 3) == 0, do: "Pling", else: "" + plang = if rem(number, 5) == 0, do: "Plang", else: "" + plong = if rem(number, 7) == 0, do: "Plong", else: "" + result = pling <> plang <> plong + + if result == "" do + Integer.to_string(number) + else + result + end +end From d37fe8e7063a4d75c9d6a08fd88ca2c0a00930a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Por=C4=99ba?= Date: Fri, 26 Jan 2024 20:30:42 +0000 Subject: [PATCH 4/6] adding myself to the contributors in config.json --- exercises/practice/raindrops/.approaches/config.json | 8 ++++++-- exercises/practice/raindrops/.meta/config.json | 3 ++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/exercises/practice/raindrops/.approaches/config.json b/exercises/practice/raindrops/.approaches/config.json index 460c5f4e6e..1457f00c4b 100644 --- a/exercises/practice/raindrops/.approaches/config.json +++ b/exercises/practice/raindrops/.approaches/config.json @@ -5,14 +5,18 @@ "slug": "pattern-matching", "title": "Pattern Matching", "blurb": "", - "authors": [] + "authors": [ + "michalporeba" + ] }, { "uuid": "925ccb59-3414-472b-9054-1cdfc5e44fad", "slug": "step-by-step", "title": "Step By Step", "blurb": "", - "authors": [] + "authors": [ + "michalporeba" + ] } ] } diff --git a/exercises/practice/raindrops/.meta/config.json b/exercises/practice/raindrops/.meta/config.json index bb893dc8e7..8301000d94 100644 --- a/exercises/practice/raindrops/.meta/config.json +++ b/exercises/practice/raindrops/.meta/config.json @@ -14,7 +14,8 @@ "parkerl", "sotojuan", "Teapane", - "waiting-for-dev" + "waiting-for-dev", + "michalporeba" ], "files": { "solution": [ From c8008f4560e592e779f34642c2f5d2c5af2e4b58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Por=C4=99ba?= Date: Sat, 27 Jan 2024 22:10:43 +0000 Subject: [PATCH 5/6] check every possibility is a more apt description of this approach --- .../content.md | 0 .../snippet.txt | 0 exercises/practice/raindrops/.approaches/config.json | 8 ++++---- .../practice/raindrops/.approaches/introduction.md | 10 +++++----- 4 files changed, 9 insertions(+), 9 deletions(-) rename exercises/practice/raindrops/.approaches/{pattern-matching => check-every-possibility}/content.md (100%) rename exercises/practice/raindrops/.approaches/{pattern-matching => check-every-possibility}/snippet.txt (100%) diff --git a/exercises/practice/raindrops/.approaches/pattern-matching/content.md b/exercises/practice/raindrops/.approaches/check-every-possibility/content.md similarity index 100% rename from exercises/practice/raindrops/.approaches/pattern-matching/content.md rename to exercises/practice/raindrops/.approaches/check-every-possibility/content.md diff --git a/exercises/practice/raindrops/.approaches/pattern-matching/snippet.txt b/exercises/practice/raindrops/.approaches/check-every-possibility/snippet.txt similarity index 100% rename from exercises/practice/raindrops/.approaches/pattern-matching/snippet.txt rename to exercises/practice/raindrops/.approaches/check-every-possibility/snippet.txt diff --git a/exercises/practice/raindrops/.approaches/config.json b/exercises/practice/raindrops/.approaches/config.json index 1457f00c4b..f159bad3de 100644 --- a/exercises/practice/raindrops/.approaches/config.json +++ b/exercises/practice/raindrops/.approaches/config.json @@ -2,9 +2,9 @@ "approaches": [ { "uuid": "fbfbabb4-f4e6-4329-b2f5-a93e3199b809", - "slug": "pattern-matching", - "title": "Pattern Matching", - "blurb": "", + "slug": "check-every-possibility", + "title": "Every Possibility", + "blurb": "Check every possibility.", "authors": [ "michalporeba" ] @@ -13,7 +13,7 @@ "uuid": "925ccb59-3414-472b-9054-1cdfc5e44fad", "slug": "step-by-step", "title": "Step By Step", - "blurb": "", + "blurb": "Perform the checks one by one, step by step.", "authors": [ "michalporeba" ] diff --git a/exercises/practice/raindrops/.approaches/introduction.md b/exercises/practice/raindrops/.approaches/introduction.md index 38fadf4c8a..fff010bf15 100644 --- a/exercises/practice/raindrops/.approaches/introduction.md +++ b/exercises/practice/raindrops/.approaches/introduction.md @@ -1,9 +1,9 @@ # Introduction -## Pattern matching +## Check every possibility -The output of the `convert` method depends on three conditions. -We can be very explicit with the solution and match all the possible patterns. +The output of the `convert` method depends on three conditions which can be either true or false. +This gives only eight possibilities and we can check them all. ```elixir def convert(number) do @@ -20,7 +20,7 @@ def convert(number) do end ``` -We can use a few Elixir features to do more or less the same and we explore them in the [pattern matching approach][pattern-matching-approach]. +We can use a few Elixir features to do more or less the same and we explore them in the [check every possibility approach][check-every-possibility-approach]. ## Step by step @@ -45,5 +45,5 @@ end Let's have a look at a few variations of this [step by step approach][step-by-step-approach]. -[pattern-matching-approach]: https://exercism.org/tracks/elixir/exercises/raindrops/approaches/pattern-matching +[check-every-possibility-approach]: https://exercism.org/tracks/elixir/exercises/raindrops/approaches/check-every-possibility [step-by-step-approach]: https://exercism.org/tracks/elixir/exercises/raindrops/approaches/step-by-step \ No newline at end of file From 6da6b4a6116a501629c94c930e63e5ae85498fb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Por=C4=99ba?= Date: Sun, 4 Feb 2024 22:11:27 +0000 Subject: [PATCH 6/6] Update exercises/practice/raindrops/.approaches/config.json Co-authored-by: Angelika Tyborska --- exercises/practice/raindrops/.approaches/config.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/exercises/practice/raindrops/.approaches/config.json b/exercises/practice/raindrops/.approaches/config.json index f159bad3de..0710c3bccb 100644 --- a/exercises/practice/raindrops/.approaches/config.json +++ b/exercises/practice/raindrops/.approaches/config.json @@ -1,4 +1,9 @@ { + "introduction": { + "authors": [ + "michalporeba" + ] + }, "approaches": [ { "uuid": "fbfbabb4-f4e6-4329-b2f5-a93e3199b809",