In part 21 of my Elixir journey I wanted to improve a messy `case` example with help from pattern matching.
Nested Case?
In my previous post I did a comparison of `case` and `cond` by writing a simple program that would evaluate if a number was even or odd. If the number was even it would also check to see if that number was greater than 10.
defmodule MyProgram do
def even(n), do: rem(n, 2) == 0
def gt(n, max), do: n > max
def calc([]), do: []
def calc([head | tail]) do
IO.puts "calculation for #{head}"
case even(head) do
true ->
case gt(head, 10) do
true ->
IO.puts "even and > 10"
false ->
IO.puts "even and < 10"
end
false ->
IO.puts "odd number"
end
calc(tail)
end
end
The program would take a list of numbers as input and simply print out the result using `IO.puts`
MyProgram.calc([2, 16, 3])
The first implementation seems like the wrong use of `case` altogether because of the nesting required to solve this problem. My next thought was to reach for pattern matching as a means to improve clarity for the reader.
defmodule MyProgram do
def even(n), do: rem(n, 2) == 0
def gt(n, max), do: n > max
def go(true, true), do: IO.puts "even and > 10"
def go(true, false), do: IO.puts "even and < 10"
def go(false, _), do: IO.puts "odd number"
def calc([]), do: []
def calc([head | tail]) do
IO.puts "calculation for #{head}"
go(even(head), gt(head, 10))
calc(tail)
end
end
While pattern matching did eliminate the nesting problem, having a function with 2 unlabeled boolean values didn't feel like an improvement in readability. I started searching for a trick that would allow me to pattern match with named arguments or something equivalent.
defmodule MyProgram do
def even(n), do: rem(n, 2) == 0
def gt(n, max), do: n > max
def go(%{ even: true, gt: true }), do: IO.puts "even and > 10"
def go(%{ even: true, gt: false }), do: IO.puts "even and < 10"
def go(%{ even: false, gt: _ }), do: IO.puts "odd number"
def calc([]), do: []
def calc([head | tail]) do
IO.puts "calculation for #{head}"
go(%{ even: even(head), gt: gt(head, 10) })
calc(tail)
end
end
I found that using the map data structure would allow me to pattern match against the key name. I'll continue to explore the problem space that plagues me with nested `case` as a path to learning more about control flow in the language.