回答
尾部呼叫淘汰是一种优化,节省堆栈空间。它取代的功能调用与转到。尾递归消除是同样的事情,但该函数调用自身的制约因素。
基本上,如果最后一件事功能A
确实是return A(params...)
那么你可以消除堆栈帧的分配,而是设置适当的寄存器并直接跳转到函数的主体中。
考虑一个(虚构)调用约定,该约定传递堆栈上的所有参数并返回某个寄存器中的值。
一些功能可以编译下至(一个虚的汇编语言):
function:
//Reading params B, C, & D off the stack
pop B
pop C
pop D
//Do something meaningful, including a base case return
...
//Pass new values for B, C, & D to a new invocation of function on the stack
push D*
push C*
push B*
call function
ret
无论上述实际上并,它占用了每个呼叫起作用的整个新的堆栈帧。然而,由于除了返回之外,在尾部调用之后没有任何操作发生,所以我们可以安全地优化该情况。
在所得:
function:
//Reading params B, C, & D off the stack (but only on the first call)
pop B
pop C
pop D
function_tail_optimized:
//Do something meaningful, including a base case return
...
//Instead of a new stack frame, load the new values directly into the registers
load B, B*
load C, C*
load D, D*
//Don't call, instead jump directly back into the function
jump function_tail_optimized
最终的结果是等同功能,节省了大量的堆栈空间,尤其是对于导致大量递归调用的输入。
我的答案中有很多想象力,但我认为你可以明白。
从here:
” ...尾递归消除是尾调用消除 的 特殊情况,其中尾调用是 函数本身在这种情况下, 呼叫的呼叫。可以通过跳转到 启动功能的移动 新的论据,以适当的 寄存器或堆栈位置后更换......”
” ......当一个函数被调用时,计算机必须‘记住’它是从,返回地址叫的地方,以便它可以返回到该位置,结果一旦通话已完成。通常情况下,这些信息会保存在堆栈中,按照它们描述的呼叫位置达到的时间顺序列出一个简单的返回位置列表。有时,函数在完成所有其他操作后所做的最后一件事情就是简单地调用一个函数,可能是它本身,然后返回结果。使用尾递归时,不需要记住我们正在调用的地方 - 相反,我们可以单独离开堆栈,而新调用的函数将直接返回原始调用者的结果。 在这种情况下将呼叫转换为分支或跳转称为尾部呼叫优化。请注意,在源代码中的所有其他语句之后,尾部调用不必出现在词法上;它只是重要的,其结果立即返回,因为调用函数将永远不会有机会做任何事情后,如果进行优化......“
- 1. 为什么不是这种尾递归?
- 2. 如何识别什么是,什么不是尾递归?
- 3. iPhone的Xcode是否消除了尾部递归?
- 4. 这个尾巴为什么递归?
- 5. 左递归消除
- 6. 为什么不是这个F#内函数尾递归?
- 7. Javascript尾递归
- 8. 尾递归算法归并
- 9. 删除尾递归方法,从(JAVA)
- 10. 为什么chmod上的递归模式除了递归之外什么都做?
- 11. 方案尾递归
- 12. 尾递归连续
- 13. 尾递归函数
- 14. 尾v头递归
- 15. 尾递归与List.fold_left
- 16. 计划。尾递归?
- 17. 为什么尾递归优化比Python中的正常递归更快?
- 18. 尾递归与前向递归
- 19. 尾递归vs原始递归
- 20. 什么是一些实施尾呼叫消除的好方法?
- 21. 什么是消息传递?
- 22. 斯卡拉尾递归为未尾递归
- 23. Java尾递归:低于斐波那契码尾递归?
- 24. 是否足够用于尾递归?
- 25. PHP是否优化尾递归?
- 26. 尾递归函数总是要避免?
- 27. 是这个函数尾递归吗?
- 28. 是作为尾递归goto指令吗?
- 29. 递归和类实例递归的区别是什么
- 30. 为什么不将OCaml List.fold_right作为尾递归实现?
写得很好,写得很清楚 – 2009-08-06 18:37:52
那么这和尾部呼叫优化一样吗? – 2009-08-06 18:45:56
是的,只是有一个附加的约束,即调用它的起源函数。对不起,这不是很清楚。 – 2009-08-06 18:48:31