That's a very clever piece of code, though it might be easier to understand if you follow it's execution step by step.
Before we look at a few examples, it's important that you understand that Elixir implements Lists as Linked Lists and each list has a head
and a tail
where the tail itself is a separate list term.
Every list (other than an empty list) can be written as [ head | tail ]
:
[1] == [ 1 | [] ]
# => true
[3, 2, 1] == [ 3 | [2, 1] ]
# => true
[3, 2, 1] == [ 3 | [ 2 | [ 1 | [] ] ] ]
# => true
1. Empty List []
Calling even_length?([])
would match the first signature and directly return true
, since that's the base case of our recursive function.
2. With One Element [x]
When calling the function on a list with one element, the VM would skip the first function definition (since it's not empty), and move on to the second one which in turn calls the function recursively on just the tail part and inverting the boolean value. If we expand the call stack, it would look like this:
even_length?([1]) # `1` is head, `[]` is tail
# => !even_length?([])
# => !(true) # We know value is `true` from base-case
# => false
3. With Two Elements [x, y]
Same thing, but we will invert the result once more (since the function will be called an extra time):
even_length?([2, 1]) # `2` is head, `[1]` is tail
# => !even_length?([1]) # `1` is head, `[]` is tail
# => !(!even_length?([]))
# => !(!(true)) # We know value is `true` from base-case
# => !(false)
# => true
4. With Three Elements [x, y, z]
even_length?([3, 2, 1]) # `3` is head, `[2, 1]` is tail
# => !even_length?([2, 1]) # `2` is head, `[1]` is tail
# => !(!even_length?([1])) # `1` is head, `[]` is tail
# => !(!(!even_length?([])))
# => !(!(!(true))) # We know value is `true` from base-case
# => !(!(false))
# => !(true)
# => false
5. With N Elements [ ... ]
And this will continue repeating. The simplest way to understand what this function does is that it has defined that a List with 0 elements should return true
, and for every extra element it should invert (boolean not
) the previous value.