首先,用一个参数来做这件事的简单方法:
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 是一个守卫,这意味着这个函数子句只有在它为真时才会被执行(在这种情况下,列表的第一项是偶数)。
然后我们返回一个包含可能翻倍的值的新列表,并使用递归来计算列表的其余部分。
上述方法在调用堆栈中建立结果。我怀疑要求您使用两个参数的原因是利用tail-call optimisation,这意味着即使它进行了递归调用,也不会使用额外的堆栈帧。因为我们没有在其中构建结果的调用堆栈,所以我们添加了一个额外的 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]