Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/elixir/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Recursion 递归在Elixir中是如何工作的_Recursion_Elixir - Fatal编程技术网

Recursion 递归在Elixir中是如何工作的

Recursion 递归在Elixir中是如何工作的,recursion,elixir,Recursion,Elixir,Elixir中的简单函数,将数字列表从返回到: defmodule MyList do def span(_), do: raise "Should be 2 args" def span(from, to) when from > to, do: [ to | span(to + 1, from) ] def span(from, to) when from < to, do: [ from | span(from + 1, to) ] def span(fr

Elixir中的简单函数,将数字列表从
返回到

defmodule MyList do

  def span(_), do: raise "Should be 2 args"
  def span(from, to) when from > to,  do: [ to | span(to + 1, from) ]
  def span(from, to) when from < to,  do: [ from | span(from + 1, to) ]
  def span(from, to) when from == to, do: [ from ]
end
我就是想不起来:

[ from | span(from + 1, to) ]
好的,我假设第一个循环将返回以下内容:

[ 1 | span(2, 5) ]
下一步是什么<代码>[1,2 | span(3,5)]?为什么?

它如何知道何时停止?为什么它还能工作

如果你不打算为函数式程序员noob(OO程序员)努力把事情弄清楚的话,请不要追根究底——不要费心回答

作为对答案的奖励,你可以给我一些关于如何开始递归思考的提示吗?有什么灵丹妙药吗

它是如何跟踪头部的?函数如何在每次迭代时创建新列表,以保持在上一次迭代中生成的值


谢谢

好的,让我们试一试

Erlang使用策略评估函数调用。从链接的维基百科:

[按值调用]是一系列求值策略,其中函数的参数在传递给函数之前进行求值

这意味着,当Elixir(或者更确切地说Erlang)看到带有一些参数的函数调用时,它会在调用函数之前对参数(显然也可以是表达式)进行求值

例如,让我们以这个函数为例:

def add(a, b), do: a + b
如果我用两个表达式作为参数调用它,那么在将结果相加之前将对这些表达式求值:

add(10 * 2, 5 - 3)
# becomes:
add(20, 2)
# kind of becomes:
20 + 2
# which results in:
22
现在我们得到了按值调用,让我们暂时把列表中的
|
构造看作一个函数。想象一下,如果它会这样使用:

|(1, [])         #=> [1]
|(29, [1, 2, 3]) #=> [29, 1, 2, 3]
与所有函数一样,
|
在执行其工作之前对其参数进行求值(即创建一个新列表,其中第一个参数作为第一个元素,第二个参数作为列表的其余部分)

当您调用
span(1,5)
时,它会扩展(比方说扩展)到:

现在,由于在能够实际将
1
前置到
span(2,5)
之前,必须对
|
的所有参数求值,因此我们必须求值
span(2,5)
。 这会持续一段时间:

|(1, |(2, span(3, 5)))
|(1, |(2, |(3, span(4, 5))))
|(1, |(2, |(3, |(4, span(5, 5)))))
|(1, |(2, |(3, |(4, [5]))))))
# now, it starts to "unwind" back:
|(1, |(2, |(3, [4, 5])))
|(1, |(2, [3, 4, 5]))
|(1, [2, 3, 4, 5])
[1, 2, 3, 4, 5]
(很抱歉,如果我正在使用此
|()
语法,请记住我只是将
|
用作函数而不是运算符)

没有任何东西跟踪头部,也没有任何函数“保留在上一次[迭代]中生成的值”。第一个调用(
span(1,5)
)只是扩展到
[1 | span(2,5)]
。现在,为了让
span(1,5)
调用返回,它需要计算
[1 | span(2,5)]
:这就是递归!它需要首先计算
span(2,5)
,依此类推

从技术上讲,值保存在某个地方,并且在堆栈上:每个函数调用都放在堆栈上,只有在能够返回时才弹出。因此,堆栈将类似于我上面显示的一系列调用:

# First call is pushed on the stack
span(1, 5)
# Second call is pushed on top of that
span(1, 5), span(2, 5)
# ...
span(1, 5), span(2, 5), ..., span(5, 5)
# hey! span(5, 5) is not recursive, we can return [5]. Let's pop span(5, 5) from the stack then
span(1, 5), ..., span(4, 5)
# Now span(4, 5) can return because we know the value of span(5, 5) (remember, span(4, 5) is expanded to [4|span(5, 5)]
这一直持续到它返回到
span(1,5)
(现在是
span(1,2,3,4,5])
),最后返回到
[1,2,3,4,5]

好吧,我写了很多,但我不确定我是否向你说得更清楚:)。请问任何不清楚的问题。肯定有很多资源可以学习递归;仅举我发现的第一批:

  • 这是一本关于二郎的好书
  • 递归
  • 一个很好的页面,我刚刚在
  • 为什么不呢,一些长生不老药特有的资源:,博客文章,博客文章

这是我读到的最棒的答案之一,也是我所有问题答案中最棒的解释。我现在就投票,明天我会投票给你另一个答案(我现在正在使用电话),明天(当我打开笔记本电脑再次阅读时),我可能会问一些问题,并确实接受答案。我非常感谢你!再次-极好的解释,阅读愉快!大量的投票证明了答案是伟大的。我真的很感谢你的回答!我从中学到了很多@AndreyDeineko很高兴:)我只是想附和Andrey的评论,非常好的回答@whatyouhide也让我更清楚了,谢谢。正如@whatyouhide所说的,它更像是
[1 |[2 |[3 |[4 |[5 |][5 |]
,计算结果是
[1,2,3,4,5]
|(1, |(2, span(3, 5)))
|(1, |(2, |(3, span(4, 5))))
|(1, |(2, |(3, |(4, span(5, 5)))))
|(1, |(2, |(3, |(4, [5]))))))
# now, it starts to "unwind" back:
|(1, |(2, |(3, [4, 5])))
|(1, |(2, [3, 4, 5]))
|(1, [2, 3, 4, 5])
[1, 2, 3, 4, 5]
# First call is pushed on the stack
span(1, 5)
# Second call is pushed on top of that
span(1, 5), span(2, 5)
# ...
span(1, 5), span(2, 5), ..., span(5, 5)
# hey! span(5, 5) is not recursive, we can return [5]. Let's pop span(5, 5) from the stack then
span(1, 5), ..., span(4, 5)
# Now span(4, 5) can return because we know the value of span(5, 5) (remember, span(4, 5) is expanded to [4|span(5, 5)]