In part 17 of my Elixir journey I wanted to show how you might implement `Enum.reduce`.
Reduce
Last time around I implemented `map` to get a sense of how recursion works when the rubber hits the road. Today I'll dive a little deeper by hand rolling `reduce` to learn even more about the wonders of recursion.
Enum.reduce([1, 2, 3], 0, &(&1 + &2))
The interface for this `reduce` function is fairly straight forward. The first parameter is a list to enumerate. The second is an initial value that we choose to start from. The third is a function we apply to each item in the list.
defmodule MyEnum do
def reduce([], value, _func), do: value
def reduce([head | tail], value, func) do
reduce(tail, func.(head, value), func)
end
end
The first function clause acts as our starting point returning our initial value. We include 3 parameters here only to ensure we pattern match correctly. The last parameter `_func` is not needed so to avoid the compiler warning we prefix it with an underscore.
In the second function clause we invoke `reduce` with the tail, a new `value` and the `func` parameter. The magic here is that we compute a new `value` with each call to `reduce` using the `func` supplied.
MyEnum.reduce([1, 2, 3], 0, &(&1 + &2))
We can calculate the return value from `reduce` by walking through the steps our program will execute in reverse. It's worth mentioning that each time the `reduce` function calls itself, it adds to the stack. And when the `reduce` function returns it will pop off the stack (hence the reverse order).
0 # reduce([])
(3 + 0) # reduce([3])
(2 + 3) # reduce([2, 3])
(1 + 5) # reduce([1, 2, 3])
With each `Enum` function I implement I can feel myself stretching muscles I've not used in years!