学习指南 →

· Albion Gateway College

递归课程练习:从终止条件追踪调用栈与返回值

用原创整数序列练习递归调用、等待中的表达式和返回顺序,检查输入约定、终止证明与空间成本。

先明确函数承诺计算什么

递归最难的地方往往不是看见函数调用自己,而是分清“进入下一次调用”和“拿到结果继续计算”。学习时先写输入与输出的约定,再画调用过程。本文使用一个原创小练习:整数 n 表示层数,第零层数值为 1,每增加一层,把上一层数值乘以 2,再加上当前层号。允许的输入是较小的非负整数。

将函数记作 layer,规则是 layer(0)=1;当 n 大于零时,layer(n)=2*layer(n-1)+n。这里的零层返回值属于题目定义,不能因为“没有层”就随意写成零。先手算前三层应为 3、8、19,随后才让程序检验。这个练习仅用于理解执行机制,不对应任何实际计分题。

把规则写成可运行的最小例子

在 Python 3 中,可以先输入一行函数定义:def layer(n): return 1 if n == 0 else 2 * layer(n - 1) + n。定义结束后另行运行 print(layer(3)),输出应为 19。这个简短版本依赖调用者遵守非负整数约定,尚未包含入口检查,不宜直接当成通用接口。

把条件表达式读成两条路径:n 等于零时直接返回 1,不再递归;否则先取得 layer(n-1) 的值,再完成乘法与加法。写成多行时,可用 if n == 0: return 1 作为基础分支,另一分支返回相同表达式。改变排版不应改变两条路径的含义。

向下调用时,记录尚未完成的计算

从 layer(3) 开始,当前调用要计算“2 乘下层结果再加 3”,因此需要 layer(2);后者要计算“2 乘下层结果再加 2”,因此需要 layer(1);继续调用 layer(0),这时才直接得到 1。到达基础情形之前,前三个调用的乘加计算都尚未完成。

纸上可为每次调用画一张卡片,分别写参数、等待的子调用、回来后要做的事。参数依次是 3、2、1、0,它们属于不同调用帧,不是一个共享的 n 被反复覆盖。最内层完成后,程序回到它的直接调用者,再回到更外层;这就是追踪调用栈时需要保留的顺序。

向上返回时,逐层代入而非重新调用

返回链可完整写成:layer(0) 返回 1;layer(1) 接收 1,计算 2*1+1,返回 3;layer(2) 接收 3,计算 2*3+2,返回 8;layer(3) 接收 8,计算 2*8+3,返回 19。把每个返回值写在相应卡片旁,再划掉已经完成的卡片。

常见误读是看到基础情形返回 1,就认为所有外层也立刻返回 1。实际上,返回只结束当前调用;外层还有自己的表达式。另一个陷阱是把 return 换成 print:打印只是输出信息,不能代替交给调用者的值。Python 函数执行到末尾却没有返回表达式时,会返回 None,外层继续做乘法就可能失败。

有基础情形,还需要证明能够到达

对于约定内的输入,每次递归使非负整数 n 减少 1,因此经过有限次调用必定到零。这是终止理由。仅说“代码里有 n == 0”并不充分:若错误地递归调用 layer(n+1),或者输入负数后继续减 1,执行路径都不会走向零。

也不要为修复负数问题直接把基础条件改成 n <= 0,却不解释新的含义。这样会给负层数返回一个看似有效的结果。更清楚的做法是在实际接口中验证类型和范围,并按约定拒绝非法输入。整数条件也很重要:从 0.5 每次减 1,同样不会恰好到零。可用纸上前几步发现问题,无须真的制造深递归。

区分数学终止与运行环境限制

输入 n 时,本例总共调用 n+1 次,最深处有 n+1 个尚未返回的调用帧。在把小整数运算视为固定成本的教学模型中,时间与栈空间都随 n 线性增长。返回值变得极大时,整数运算的实际成本也会变化,不能把这个简化模型理解成精确计时公式。

数学上会终止,并不保证任意大的 n 都能在 Python 中递归完成。解释器存在递归深度限制;调高限制并不能修复不收敛的调用,还可能增加风险。调试时先看参数是否推进,再看是否需要改为循环逐层更新结果。不要以扩大限制代替证明正确性。

自练:改变一条规则,再核对两条链

先不运行程序,预测 layer(4) 和 layer(0),并写出各自的调用次数。答案是 42 与 1;调用次数分别是 5 与 1。然后只把基础值从 1 改成 2,递推式保持不变,重新计算第三层。新返回链是 2、5、12、27,因此结果为 27,而不是简单把原结果加 1。

迁移到列表、树或目录遍历时,仍按同样的顺序检查:输入约定是什么;哪种情况无需再调用;每次递归让什么量变小;子结果回来后如何组合;空输入和最小非空输入分别怎样处理。Tutorial 中可以展示一条下降链与一条返回链,请对方检查你的解释,而不是要求代写待提交的解答。

参考资料

下一步

准备递归相关的 Tutorial 问题时,可带上自己的调用记录和最先无法解释的一步。计分代码与论证须独立完成。

继续阅读

学院在线咨询

欢迎咨询

你好,欢迎咨询!你是学生、招生代理、求职者,还是想申请成为导师?请告诉我你的咨询方向。

[email protected]