理解 Elixir 中的递归

Understanding recursion in Elixir

如果这是基本的,请原谅我,但我是 Elixir 的新手,正在尝试理解这门语言。 说我有这个代码

defmodule Test do
  def mult([]), do: 1
  def mult([head | tail]) do
    IO.puts "head is: #{head}"
    IO.inspect tail
    head*mult(tail)
  end
end

当我 运行 这样做时:Test.mult([1,5,10])

我得到以下输出

head is: 1
[5, 10]
head is: 5
'\n'
head is: 10
[]
50

但我很难理解发生了什么,因为如果我单独尝试这样做:

[h | t] = [1,5,10]
h * t

显然我得到了一个错误,有人可以解释我遗漏了什么吗?

考虑在每次调用时传递给 mult 的参数。

当您首先执行 Test.mult([1,5,10]) 时,它会检查第一个函数子句;即可以使 [1,5,10] 匹配 []。如果可以的话,Elixir 会尝试让这两个表达式匹配。在这种情况下它不能那么它会尝试下一个函数子句; [1,5,10] 可以匹配 [head|tail] 吗?是的,它可以,因此它将第一个元素 (1) 分配给 head,其余元素 [5,10] 分配给 tail。然后它再次递归调用该函数,但这次使用列表 [5,10]

它再次尝试将 [5,10] 匹配到 [];这再次无法匹配,因此它下降到 [head|tail]。这次 head 是 5,tail 是 10。所以再次使用 [10]

递归调用该函数

同样,[10]可以匹配[]吗?不,所以它再次点击 [head|tail] 并分配 head = 10 和 tail = [](记住每个列表的末尾总是有一个隐含的空列表)。

最后一轮;现在 [] 肯定匹配 [] 所以它 returns 1. 然后评估先前的 head * mult(tail) (1 * 10) 并将结果返回到堆栈上的先前调用。再次评价head (5) * mult(tail) (10) = 50。堆栈的最终展开 head (1) * mult(tail) (50) = 50。因此函数的总值为 50.

请记住,Elixir 在评估所有后续函数调用之前无法完全评估任何函数调用。所以它依赖中间值来计算函数的最终值。

现在考虑模式匹配方面的第二个代码片段。 [h|t] = [1,5,10] 将分配 h = 1t = [5,10]h*t 表示 1 * [5,10]。由于这些是根本不同的类型,因此在这种情况下没有内置的乘法定义。

您的函数分解为:

mult([1 | [5, 10]])
1 * mult([5 | [10]])
1 * 5 * mult([10 | []])
1 * 5 * 10 * mult([])
1 * 5 * 10 * 1
50

'\n' 实际上是 [10] 并且是由于:Elixir lists interpreted as char lists.

IO.inspect('\n', charlists: :as_lists)
[10]