Во-первых, простой способ сделать это с одним аргументом:
defmodule Double do
def double([]), do: []
def double([head | tail]) when rem(head, 2) == 0, do: [head * 2 | double(tail)]
def double([head | tail]), do: [head | double(tail)]
end
При этом используется сопоставление с шаблоном аргумента для назначения первого элемента списка переменной head
.
when rem(head, 2) == 0
- это защита, означающая, что это функциональное предложение будет выполняться только тогда, когда оно истинно (в данном случае первый элемент списка является четным).
Затем мы возвращаем новый список, состоящий извозможно удвоенное значение и использовать рекурсию для вычисления остальной части списка.
Приведенный выше метод создает результат в стеке вызовов.Я подозреваю, что причина, по которой вас просят использовать два аргумента, состоит в том, чтобы воспользоваться оптимизацией хвостового вызова , что означает, что даже при совершении рекурсивного вызова он не использует никаких дополнительных стековых фреймов.Поскольку у нас нет стека вызовов для построения результата, мы добавляем дополнительный аргумент output
и собираем его там:
defmodule Double do
def double([], output), do: Enum.reverse(output)
def double([head | tail], output) when rem(head, 2) == 0, do: double(tail, [head * 2 | output])
def double([head | tail], output), do: double(tail, [head | output])
end
Здесь мы пишем функцию, которая принимает input
иoutput
список.Функция вызывает себя до тех пор, пока input
не будет исчерпана (это пустой список []
), и выстраивает ответ в списке output
, который она в конечном итоге возвращает.При каждом вызове мы добавляем текущий элемент в список вывода.
iex> Double.double([1,2,3,4], [])
[1, 4, 3, 8]