【发布时间】:2015-09-20 09:22:10
【问题描述】:
我在阅读尾递归和传统递归之间的区别时发现它提到“尾递归是一种不使用任何堆栈空间的递归形式,因此是一种安全使用递归的方法。” '
我很难理解如何。
比较使用传统递归和尾递归查找数字的阶乘
传统递归
/* traditional recursion */
fun(5);
int fun(int n)
{
if(n == 0)
return 1;
return n * fun(n-1);
}
在这里,调用堆栈看起来像
5 * fact(4)
|
4 * fact(3)
|
3 * fact(2)
|
2 * fact(1)
|
1 * fact(0)
|
1
尾递归
/* tail recursion */
fun(5,1)
int fun(int n, int sofar)
{
int ret = 0;
if(n == 0)
return sofar;
ret = fun(n-1,sofar*n);
return ret;
}
但是,即使在这里,变量“sofar”也会在不同点保持 - 5,20,60,120,120。 但是一旦从递归调用#4的基本情况调用return,它仍然必须将120返回到递归调用#3,然后返回到#2、#1并返回到main。 所以,我的意思是说使用了栈,每次返回之前的调用,都可以看到那个时间点的变量,也就是说每一步都在保存。
除非尾递归写成下面这样,否则我无法理解它是如何节省堆栈空间的。
/* tail recursion */
fun(5,1)
int fun(int n, int sofar)
{
int ret = 0;
if(n == 0)
return 'sofar' back to main function, stop recursing back; just a one-shot return
ret = fun(n-1,sofar*n);
return ret;
}
PS:我已经阅读了一些关于 SO 的线程并开始了解尾递归是什么,但是,这个问题更多地与它为什么节省堆栈空间有关。我在讨论这个问题时找不到类似的问题。
【问题讨论】:
-
你对什么是尾递归感到困惑。看到这个:stackoverflow.com/questions/2693683/tail-recursion-in-c
-
我想知道这句话。我只知道尾链,如果像
return f()一样使用它只会跳转到f,而不是创建一个新的堆栈帧,调用F()并返回结果。如果是尾链,f()然后直接返回给原始调用者。但是,这是编译器优化的问题;你应该依靠它。编译器可能有一个非常不同的想法如何/如果尾链。 -
尾递归是传统的递归。或者更确切地说,它是一种递归形式;但它可能是所有形式中最传统的。参见,例如,en.wikipedia.org/wiki/Recursion#Recursively_defined_sets:翻译成伪代码,它可能是:
def is_in_N(x): if x == 0 then return true else return is_in_N(x-1) -
需要尾调用才能对其进行尾调用优化,但尾调用本身并不能保证优化。对于像 Scheme 这样的语言,它要求 TCO 没有问题,你可以说尾递归不使用额外的堆栈空间,但对于所有其他不需要尾递归的语言,它 itelf 并不意味着你不会使用堆栈. C++ 就属于这一类。只进行尾部调用的函数是一个迭代过程,而至少一个调用不在尾部位置的递归是一个递归过程。这与传统无关。
标签: c++ c recursion tail-recursion