Это очень умный кусок кода, хотя его будет проще понять, если вы будете следить за его выполнением шаг за шагом.
Прежде чем мы рассмотрим несколько примеров, важно, чтобы вы понимали, что Elixir реализует Списки в виде связанных списков , и каждый список имеет head
и tail
, где сам хвост является отдельным термином списка.Каждый список (кроме пустого) можно записать как [ head | tail ]
:
[1] == [ 1 | [] ]
# => true
[3, 2, 1] == [ 3 | [2, 1] ]
# => true
[3, 2, 1] == [ 3 | [ 2 | [ 1 | [] ] ] ]
# => true
1.Пустой список []
Вызов even_length?([])
будет соответствовать первой сигнатуре и напрямую вернет true
, поскольку это базовый случай нашей рекурсивной функции.
2,С одним элементом [x]
При вызове функции из списка с одним элементом виртуальная машина пропустит первое определение функции (поскольку оно не пустое) и перейдет ко второму, которое в свою очередь вызываетрекурсивно функционирует только на хвостовой части и инвертирует логическое значение.Если мы расширим стек вызовов, это будет выглядеть так:
even_length?([1]) # `1` is head, `[]` is tail
# => !even_length?([])
# => !(true) # We know value is `true` from base-case
# => false
3.С двумя элементами [x, y]
То же самое, но мы будем инвертировать результат еще раз (поскольку функция будет вызываться в дополнительное время):
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.С тремя элементами [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.С N элементами [ ... ]
И это будет повторяться.Самый простой способ понять, что делает эта функция, - это определить, что список с 0 элементами должен возвращать true
, а для каждого дополнительного элемента он должен инвертировать (логическое not
) предыдущее значение.