You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

递归函数的栈帧工作原理解析及Python递归列表打印的栈帧差异疑问解答

为什么调整递归中print的顺序会导致输出正/倒序?

这问题的核心其实就是递归调用的栈帧执行顺序——你可以把每个递归调用想象成一个“暂停的任务”,Python会把这些任务按调用顺序堆叠起来(这就是栈的“后进先出”特性),只有当最顶层的任务完成后,才会回头处理下面的任务。

先看正序输出的情况

原函数代码:

def printout(list):
    if len(list) > 0:
        print(list[0])
        printout(list[1:])

当传入list1 = [5,6,7,8,9]时,执行流程是这样的:

  • 第一个printout([5,6,7,8,9])先执行print(5),然后暂停自己,去调用printout([6,7,8,9])
  • 第二个printout([6,7,8,9])先执行print(6),暂停自己,调用printout([7,8,9])
  • 以此类推,直到printout([9])执行print(9),然后调用printout([])
  • 当遇到空列表时,函数直接返回(没有可执行的代码),这时候栈里的“暂停任务”开始依次收尾:从printout([9])回到printout([8,9]),但后者已经做完了所有该做的,直接返回,直到最开始的printout([5,6,7,8,9])收尾完成。

整个过程中,每个任务在暂停前都已经执行了print,所以输出就是正序的5 6 7 8 9

对应的栈帧变化(从栈底到栈顶):

  1. 栈帧1:参数list=[5,6,7,8,9],执行到printout(list[1:])行(已完成print
  2. 栈帧2:参数list=[6,7,8,9],执行到printout(list[1:])行(已完成print
  3. 栈帧3:参数list=[7,8,9],执行到printout(list[1:])行(已完成print
  4. 栈帧4:参数list=[8,9],执行到printout(list[1:])行(已完成print
  5. 栈帧5:参数list=[9],执行到printout(list[1:])行(已完成print
  6. 栈帧6:参数list=[],条件不满足,直接返回

当栈帧6返回后,栈帧5到栈帧1依次弹出,每个都没有剩余代码需要执行,直接结束。

再看倒序输出的情况

修改后的函数代码:

def printout(list):
    if len(list) > 0:
        printout(list[1:])
        print(list[0])

这时候执行顺序完全反过来了:

  • 第一个printout([5,6,7,8,9])先暂停自己,直接调用printout([6,7,8,9])还没执行print(5)
  • 第二个printout([6,7,8,9])同样暂停自己,调用printout([7,8,9]),也没执行print(6)
  • 一直到printout([9])暂停自己,调用printout([])
  • printout([])返回后,栈顶的printout([9])才会执行剩下的print(9),然后返回
  • 接着栈顶变成printout([8,9]),执行print(8),返回
  • 以此类推,直到最开始的printout([5,6,7,8,9])执行print(5),整个过程结束。

因为每个任务都是在子任务完成后才执行print,所以输出就是倒序的9 8 7 6 5

对应的栈帧变化(从栈底到栈顶):

  1. 栈帧1:参数list=[5,6,7,8,9],执行到print(list[0])行(还没执行)
  2. 栈帧2:参数list=[6,7,8,9],执行到print(list[0])行(还没执行)
  3. 栈帧3:参数list=[7,8,9],执行到print(list[0])行(还没执行)
  4. 栈帧4:参数list=[8,9],执行到print(list[0])行(还没执行)
  5. 栈帧5:参数list=[9],执行到print(list[0])行(还没执行)
  6. 栈帧6:参数list=[],条件不满足,直接返回

当栈帧6返回后,栈帧5先执行print(9)再弹出,接着栈帧4执行print(8)再弹出……直到栈帧1执行print(5)后弹出,所有任务完成。

简单总结:递归调用就像叠盘子,先放的盘子在最下面,后放的在最上面。正序是放盘子前先做标记,倒序是等所有盘子叠完,从最上面开始做标记。

内容的提问来源于stack exchange,提问作者PScott

火山引擎 最新活动