【问题标题】:Understanding recursion in Elixir了解 Elixir 中的递归
【发布时间】:2022-02-03 10:24:02
【问题描述】:

如果这是基本的,请原谅我,但我是 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

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

【问题讨论】:

  • 尝试将IO.inspect tail 替换为IO.inspect tail, charlists: false 并检查是否更好?

标签: recursion elixir


【解决方案1】:

你的函数分解为:

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 将每次迭代的值存储在哪里?我认为这是阻碍我完全理解它的原因
  • 这是一个非常复杂的问题。简短的回答是,当您调用函数时,虚拟机会记住上下文,并在函数返回时恢复该上下文。查看blog.stenmans.org/theBeamBook/…
【解决方案2】:

考虑在每次调用时传递给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 = [](请记住,每个列表的末尾总是有一个隐含的空列表)。

最后一轮;现在[] 绝对匹配[] 所以它返回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]。由于它们是根本不同的类型,因此在这种情况下没有内置的乘法定义。

【讨论】:

  • 非常有用的答案,谢谢。你的倒数第二段真的帮助了我。所以 Elixir 将把递归函数的最终值和 * 它取为退出函数的值(在我的例子中是 1)。所以它基本上存储递归函数的值,然后用退出函数计算它。太好了,真的很有帮助!
猜你喜欢
  • 2020-05-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-09
相关资源
最近更新 更多