理解 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 = 1
和 t = [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]
如果这是基本的,请原谅我,但我是 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 = 1
和 t = [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]