理解递归代码的演练

Comprehending the walk through of a recursive code

本文关键字:演练 代码 递归      更新时间:2023-10-16

如果我在经过时将手放在每一行代码上,那么当我们点击函数调用时,我的手会转到被调用的函数并且该函数开始执行,之后我的手返回到调用函数的行。

现在说到递归,当我不断调用相同的函数时,手的行为如何?每个函数调用都有新手牌吗?我不明白代码如何"回到递归调用树上"。我知道有一个堆栈。我看过所有这些解释它的视频。我不明白的是,如果我写 cout 语句,一个在递归调用上方,一个在递归调用下,那么我就明白为什么递归调用上方的 cout 语句执行的

次数与递归函数被调用的次数一样多,但是递归调用下方的 cout 语句是如何被执行多次的呢? 下面是一个示例

int Factorial(int n)
{
cout<<"first cout statement before the recursive call";
if( n == 0 )
return 1;
int F = n*Factorial(n-1);
cout<<"second cout statement after the recursive call";
return F; 
}
int main()
{
int n;
cin>>n;
int result = Factorial(n);
cout<<result;
}

这是下面的输出。

first cout statement before the recursive call
first cout statement before the recursive call
first cout statement before the recursive call
first cout statement before the recursive call
first cout statement before the recursive call
first cout statement before the recursive call
second cout statement after the recursive call  //Notice these
second cout statement after the recursive call
second cout statement after the recursive call
second cout statement after the recursive call
second cout statement after the recursive call
120

将调用堆栈视为索引卡托盘。堆栈开始时为空。你唯一能做的就是写下一张新的索引卡,把它放在上面,或者把上面的东西拿下来。

运行时放在那里的托盘中有一些东西,但目前让我们假装main()开始运行时托盘是空的。

假设每个函数都是一本书中的一个章节。 每个页面都有一堆单独的句子,每个句子都告诉你做一件特定的事情。 其中一件事可能是"转到另一章,按照它所说的去做,使用我在这里掌握的这些信息。 当你说到这样的句子时,你需要记住如何回到你原来的位置,所以你在索引卡上写下以下内容:

  • 当前所在的章节以及您刚刚阅读的章节的哪个句子(例如,可能是该段落中的页码、段落编号和句子编号(。
  • 在要求您进入另一章之前,您正在使用的任何信息。

你把这张卡片放在托盘的顶部,然后你转到另一章并开始做一些事情。

当你读到一个句子说"你已经完成了这一章,回到最后一章,这个结果",然后你把卡片从顶部拿下来,回到它所说的任何章节,找到紧跟在卡片指示的句子后面的句子,然后你从那里继续。

这就是这里发生的一切。 章节是函数。 句子是机器指令。 你带到另一章的东西是函数参数。 你从章节中取回的东西是返回值。 你放在索引卡上的"你正在使用的东西"是进行函数调用时任何函数局部变量的值。

当你进行递归调用时,你仍然在堆栈上放了一些东西,但你只是转到你已经在阅读的同一章 - 只是信息略有不同。 当您需要离开该章节时,最上面的卡片可能会说您仍在同一章节中,但在不同的句子和不同的信息上。

您有一系列嵌套调用,每个递归调用一个堆栈帧。 当控制从递归调用中返回时,您仍然处于相同的函数中 - 但具有与调用之前相同的局部变量值。

所以在你的情况下,是的,你可以认为你有"多只手"。 无论您递归到同一函数的深度有多深,您都必须在每次递归调用返回时进行备份。

老实说,递归调用和非递归调用在程序流逻辑上的进展方式上没有任何区别(尾部调用优化的情况除外(。 编译器和机器代码不在乎调用相同的函数,无论哪种方式,它都执行相同的过程。