erlangs递归函数不仅仅是一个goto?

Toa*_*oad 10 erlang recursion tail-recursion goto

只是想直截了当.考虑这个Erlang代码的例子:

 test() ->
      receive
          {From, whatever} ->
                %% do something
               test();
          {From, somethingelse} ->
               %% do something else
               test();
 end.
Run Code Online (Sandbox Code Playgroud)

是不是test()调用,只是一个转到?

我问这个是因为在C中我们了解到,如果你进行函数调用,返回位置总是放在堆栈上.我无法想象这里的Erlang一定是这种情况,因为这会导致堆栈溢出.

基本的.我们有两种不同的调用函数的方法:goto和gosub.goto刚刚把程序流引导到其他地方,gosub记得你来自哪里,所以你可以回来.

考虑到这种思维方式,我可以更容易地看一下erlang的递归,因为如果我只读:test()作为goto,那么根本就没有问题.

因此我的问题:不是erlang只是使用goto而不是记住堆栈上的返回地址?

编辑:

只是为了澄清我的观点:

我知道goto可以用在某些语言中来跳过这个地方.但是,只是suupose而不是someFunction()你也可以这样做:在第一个例子中返回流返回的一些函数(),在第二个例子中,流程只在someFunction中继续并且永远不会返回.

所以我们通过跳转到方法起点来限制正常的GOTO行为.

如果你这样看,那么erlang递归函数调用看起来就像一个goto.

(在我看来,goto是一个函数调用,无法返回你来自的地方).这正是erlang示例中正在发生的事情.

out*_*tis 14

尾递归调用更多的是"返回并立即调用此其他函数"而不是goto,因为执行了内务处理.

解决您的最新要点:记录返回点只是在调用函数时执行的一个内务处理.返回点存储在堆栈帧中,其余部分必须分配和初始化(在正常调用中),包括局部变量和参数.使用尾递归,不需要分配新帧,并且不需要存储返回点(前一个值正常工作),但需要执行其余的内务处理.

当函数返回时,还需要执行内务处理,包括丢弃本地和参数(作为堆栈帧的一部分)并返回到调用点.在尾递归调用期间,当前函数的本地函数在调用新函数之前被丢弃,但不会发生返回.

类似于线程如何允许比进程更轻量级的上下文切换,尾部调用允许更轻量级的函数调用,因为可以跳过一些内务处理.

goto &NAMEPerl中的" "声明更接近你的想法,但并不完全,因为它丢弃了当地人.为新调用的函数保留参数.

还有一个简单的区别:尾部调用只能跳转到函数入口点,而goto可以在任何地方跳转(某些语言限制了goto的目标,例如C,其中goto不能跳转到函数之外).


Zed*_*Zed 8

你是对的,Erlang编译器将检测到它是一个尾递归调用,而不是继续在堆栈上,它重用当前函数的堆栈空间.

此外,它还能够检测循环尾递归,例如

test() -> ..., test2().
test2() -> ..., test3().
test3() -> ..., test().
Run Code Online (Sandbox Code Playgroud)

也将进行优化.

这种"不幸"的副作用是,当您跟踪函数调用时,您将无法看到每次调用尾递归函数,而是查看入口和出口点.

  • 重要的一点是,这仅适用于尾调用,换句话说,对于函数返回调用另一个函数将返回的函数的情况.在这种情况下,再也没有必要抓住堆栈了.如果函数返回而不是继续循环,则只需像往常一样弹出堆栈. (4认同)