With both square and add_1, all the work is done in the second function definition. And that definition looks about the same for each—it returns a new list whose head is the result of either squaring or incrementing the head of its argument and whose tail is the result of calling itself recursively on the tail of the argument. Let’s generalize this. We’ll define a function called map that takes a list and a function and returns a new list containing the result of applying that function to each element in the original.
| def map([], _func), do: [] |
| def map([ head | tail ], func), do: [ func.(head) | map(tail, func) ] |
The map function is pretty much identical to the square and add_1 functions. It returns an empty list if passed an empty list; otherwise it returns a list where the head is the result of calling the passed-in function and the tail is a recursive call to itself. Note that in the case of an empty list, we use _func as the second parameter. The underscore prevents Elixir from warning us about an unused variable.
To call this function, pass in a list and a function (defined using fn):
| iex> c "mylist1.exs" |
| [MyList] |
| iex> MyList.map [1,2,3,4], fn (n) -> n*n end |
| [1, 4, 9, 16] |
A function is just a built-in type, defined between fn and the end. Here we pass a function as the second argument (func) to map. This is invoked inside map using func.(head), which squares the value in head, using the result to build the new list.
We can call map with a different function:
| iex> MyList.map [1,2,3,4], fn (n) -> n+1 end |
| [2, 3, 4, 5] |
And another:
| iex> MyList.map [1,2,3,4], fn (n) -> n > 2 end |
| [false, false, true, true] |
And we can do the same using the & shortcut notation:
| iex> MyList.map [1,2,3,4], &(&1 + 1) |
| [2, 3, 4, 5] |
| iex> MyList.map [1,2,3,4], &(&1 > 2) |
| [false, false, true, true] |