我想知道如何生成以下代码示例中的第二个输出。在if语句和递归函数调用结束后,函数如何能够向后计数?
void Recursion(int x)
{
if(x < 4) {
cout << x << " "; // 1st output: 1 2 3
Recursion(x + 1);
}
cout << x << " "; // 2nd output: 4 3 2 1
}
int main()
{
Recursion(1);
}
答案 0 :(得分:3)
我要将这个问题解释为“这个函数怎么能记住它返回时它正在做什么?”。
C ++标准没有说明(IIRC,它留给实现找到有效的解决方案)。但实际上,答案是“堆栈”。
在计算机科学中,堆栈通常是先进先出的数据结构。将序列1,2和3推入堆栈,然后执行三次弹出,然后得到3,2,然后是1。
在早期,编程语言通常不支持递归或重入调用。每个函数/过程都拥有一小块内存,它存储了它的参数,局部变量以及完成后返回的函数。如果你试图调用一个已经运行的函数,那就意味着在空间中存储两组局部变量和两个返回地址,这是一个错误。
然而,IIRC的一项创新是Algol编程语言支持递归。大约在同一时间,“处理器堆栈”正在成为一种东西。
处理器堆栈(以及其他内容)允许您使用不同的方法来处理参数,局部变量和返回地址。每个函数不需要永久分配的块 - 在调用函数时分配一个块(在堆栈的顶部)。当前“堆栈帧”的位置是相对于当前“堆栈指针”的。这意味着您可以同时在堆栈上为同一个函数设置多个堆栈帧。
因此调用函数涉及在堆栈顶部创建一个新的堆栈帧,并调整堆栈指针以适应。从函数返回涉及丢弃该堆栈帧并向后调整堆栈指针,因此顶部堆栈帧现在是调用者的堆栈帧。该调用者可能是也可能不是同一个函数 - 这并不重要,因为每个调用都有自己的堆栈帧,存储一组独立的参数,局部变量,一个单独的返回地址等。
所以就在Recursion (3)
调用之前,堆栈看起来像......
|-------------------+-------------------+
| Recursion Frame 1 | Recursion Frame 2 |
|---------------+---+---------------+---+
| ??? | X | ??? | X |
|---------------+---+---------------+---+
| ??? | 1 | ??? | 2 |
|---------------+---+---------------+---+
^
|
STACK
POINTER
???
代表“看家”这样的东西,比如寄回地址。
答案 1 :(得分:1)
想想第一次调用Recursion。
该函数将打印一个“1”,然后其他东西将会发生,然后它再次打印“1”。
因此输出将为1 ... 1
现在想想第二次调用Recursion,它会打印一个“2”,然后其他东西就会发生,然后再打印一个“2”。
所以它的输出是2 ... 2
。
将这两者放在一起,得到1 2 ... 2 1
然后继续前进。
答案 2 :(得分:1)
您应该尝试单步执行代码,使用铅笔和纸张(或虚拟便笺本)执行代码。让我们保持函数定义关闭:
void Recursion(int x)
{
1 if(x < 4) {
2 cout << x << " "; // 1st output: 1 2 3
3 Recursion(x + 1);
. }
.
4 cout << x << " "; // 2nd output: 4 3 2 1
5 return;
}
现在,我们打电话给main
:
call main
call Recursion(1) -> x := 1
(1) if (x < 4) -> true
(2) cout << x << " "; // with x == 1
(3) call Recursion(x + 1) -> x := 2
(1) if (x < 4) -> true
(2) cout << x << " "; // with x == 2
(3) call Recursion(x + 1) -> x := 3
(1) if (x < 4) -> true
(2) cout << x << " "; // with x == 3
(3) call Recursion(x + 1) -> x := 4
(1) if (x < 4) -> false
(4) cout << x << " "; // with x == 4
(5) end Recursion -> x:= 3
(4) cout << x << " "; // with x == 3
(5) end Recursion -> x:= 2
(4) cout << x << " "; // with x == 2
(5) end Recursion -> x:= 1
(4) cout << x << " "; // with x == 1
(5) end Recursion -> del x
end main
现在,您可以检查输出的内容:cout << x << " ";
连续调用值:1,2,3,4,3,2,1,产生"1 2 3 4 3 2 1 "
。
答案 3 :(得分:0)
如果你看一下它会有所帮助:
string RecursiveReturn(int x)
{
if(x >= 4) {
return to_string(x);
}
return to_string(x) + " " + RecursiveReturn(x+1) + " " + to_string(x);
}
int main()
{
cout << Recursion(1);
}
递归调用就像任何其他函数调用一样...它被评估,并且当它返回调用函数时继续。
答案 4 :(得分:0)
该堆栈描述了当对stdout执行“1st output”时,“2nd output”被插入堆栈(“函数堆栈”)。
当函数“recursion”返回“main”函数时,会发生堆栈的展开或弹出内容。如果你正确地进行了透视,这就是它如何提供输出。
答案 5 :(得分:0)
这里发生的是前向和后向递归。
简单来说:在调用递归之前使用计数器(因为长计数器递增)通常是前向递归,而后使用counter是递归递归。
仅限前进:
void Recursion(int counter) {
if(counter < n) {
cout << counter << " ";
Recursion(counter + 1);
}
}
首先打印计数器而不是一遍又一遍地调用递归...这导致 1 2 .. n-1 :
call : 1
print: 1
call : 1+1
print: 2
call : 2+1
print: 3
call : 3+1
if(...) // not true
return:
return:
return:
return:
仅向后:
void Recursion(int counter) {
if(counter < stop) {
Recursion(counter + 1);
cout << counter << " ";
}
}
首先一遍又一遍地调用Recursion,而不是打印计数器...这会导致 n-1 ... 2 1 :
call : 1
call : 1+1
call : 2+1
call : 3+1
if(...) // not true
return:
print: 3
return:
print: 2
return:
print: 1
return:
注意:使用if-statement 中的计数器会消除 n 的打印,它会在n-1上停止。
这是tree-traversal之类的遍历算法的基础,它被称为预订和后期订单。