c++ - 尾递归如何真正帮助传统递归?

标签 c++ c recursion tail-recursion

我在阅读尾递归和传统递归之间的区别时发现它提到“然而,尾递归是一种不使用任何堆栈空间的递归形式,因此是一种安全使用递归的方法。” '

我正在努力理解如何做。

比较使用传统递归和尾递归查找数字的阶乘

传统递归

/* 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 的线程并开始理解尾递归是什么,但是,这个问题更多地与它为什么节省堆栈空间有关。我在讨论这个问题时找不到类似的问题。

最佳答案

诀窍是如果编译器注意到尾递归,它可以编译 goto 来代替。它将生成类似于以下代码的内容:

int fun_optimized(int n, int sofar)
{
start:
    if(n == 0)
       return sofar;

    sofar = sofar*n;
    n = n-1;
    goto start;
}

正如您所见,每次迭代都会重复使用堆栈空间。

请注意,只有当递归调用是函数中的最后一个操作时才能进行此优化,即尾递归(尝试对非尾情况手动执行此操作,您将看那是不可能的)。

关于c++ - 尾递归如何真正帮助传统递归?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31191475/

相关文章:

c - 使用 fstack-check 时出现意外的 valgrind "invalid write"

c - GTK C - 使用 g_signal_connect 传递多个变量

recursion - Prolog 递归 - 满足两个方向(简单)

powershell - 递归添加文件到文件夹,如果不存在则创建文件夹

c++ - 编写一个 C++ 程序,查找字符串中使用的元音字母的数量

c++ - MFC:在派生的 CEdit 中没有收到 EN_CHANGE 消息

C: const vs no const ..这是怎么编译的?

c++ - 如何递归检查数字是否为斐波那契数?

c++ - 可以使用 memcpy() 复制包含指针的结构吗?

c++ - 混合 Haskell 和 C++