编程基础篇 · AI 背后的数据结构

栈:Cmd+Z 和「爆栈」的秘密

后进先出:撤销键、函数调用、Agent 的子任务都靠它。亲手压栈弹栈,再看一次没写终止条件的递归是怎么把栈压爆的

本页解决的问题

先给结论

「栈:Cmd+Z 和「爆栈」的秘密」要解决的关键问题是什么?

后进先出:撤销键、函数调用、Agent 的子任务都靠它。亲手压栈弹栈,再看一次没写终止条件的递归是怎么把栈压爆的

判断标准

让这个结论先证明自己值得留下。 把这一页当成决策工具,而不是需要背下来的定义。把概念连到一个真实任务、一个可观察结果,以及一个能改变你判断的失败上。

下一步

写下一个问题:试完这个方法后,你能用什么证据回答它?

常见误区

结论听起来很完整,却没有检查最关键的假设。

先玩 · 撤销键的真身

左边是个迷你文档,用四个按钮随便折腾它。留意右边:你的每一步操作都被压成一张牌,叠在「操作栈」上——后做的压在上面。折腾够了,连按几下「Cmd+Z 撤销」,看看它撤的顺序。

📄 迷你文档

随便操作,别心疼

🥞 操作栈

每步操作压一张牌,最上面 = 最近一步

(还没有操作)
试试:打两行字 → 标红 → 再撤销三次,猜猜恢复的顺序
发现了吗?撤销永远先撤最近那步。这不是产品经理的选择,是栈的天性:后进先出(LIFO,Last In First Out)。只从顶端拿,最先拿到的自然是最后放的。撤销要是不按这个顺序来——先撤掉十步前的操作——文档立刻乱成一锅粥。凡是「按原路退回去」的事,天生就该用栈收纳
程序怎么记得「回到哪」?

你让 AI「做晚饭」,它做到一半要切菜,切菜切到一半刀钝了要磨刀……每次「暂停手头的事去干别的」,程序都会把当前进度压进一个栈里。看下面的动画(滚到这里会自动播一遍),留意每个任务完成后,程序是怎么自动找回上一个任务的。

准备就绪点「播放」或滚动到这里,看一次完整的调用与返回
这个栈叫函数调用栈,每张牌叫一个「栈帧」,里面记着「这个任务干到哪了、干完回到哪」。程序不需要什么全局调度员——弹出栈顶,自然就回到了上一层暂停的位置。你学过的 Agent 也一样:主任务干到一半发现要先查资料,就把主任务压栈、先干子任务,子任务完成弹出,接着干主任务。
爆栈现场 · Stack Overflow 是怎么发生的

栈的容量是有限的。下面这个函数自己调用自己(这叫递归),左边是代码,右边是调用栈。先不勾终止条件跑一次,看看会发生什么;再勾上跑一次对比。

function 倒数(n) { if (n === 0) return; // 终止条件(保险丝) 打印(n); 倒数(n - 1); // 自己调用自己 } 倒数(5);
💥 Stack Overflow栈溢出,程序崩溃
先直接运行,看看没有保险丝的递归什么下场
这和 AI 有什么关系?关系大了。Agent 干活也是「主任务压栈、先干子任务」,那万一子任务又派生子任务、一层层没完没了呢?和没写终止条件的递归一模一样——所以所有正经的 Agent 框架都要设最大步数 / 最大深度,超过就强制停下来。那根保险丝,就是给「无限递归」上的保险。下次你看到 AI 写的递归代码,验收清单第一条:终止条件在哪?

「先玩 · 撤销键的真身」为什么要看操作

「左边是个迷你文档,用四个按钮随便折腾它。」把结构落到了一个具体动作。这里真正要比较的不是名词谁更高级,而是数据如何被放置,以及最常发生的操作需要走多远。

读懂结构,要同时看访问方式和变化方式

「你让 AI「做晚饭」,它做到一半要切菜,切菜切到一半刀钝了要磨刀……每次「暂停手头的事去干别的」,程序都会把当前进度压进一个栈里。」揭示了一个容易被忽略的取舍:按位置读取、按键查找、从两端进出、插入新元素和遍历关系,适合的组织方式并不相同。一个结构在某个操作上很快,不代表它在所有操作上都快。

  • 栈 = 一摞盘子 :只从顶端放和拿,后进先出(LIFO)
  • 撤销键的真身 :每步操作压一张牌,Cmd+Z 永远先弹最近那张
  • 函数调用靠栈记路 :弹出栈顶,自动回到上一层暂停的地方

把规模和更新频率一起算进去

实践时可以把「栈的容量是有限的。下面这个函数 自己调用自己 (这叫递归),左边是代码,右边是调用栈。先 不勾 终止条件跑一次,看看会发生什么;再 勾上 跑一次对比」当作边界提醒:先写下数据量、最常用的操作和允许的延迟,再看 AI 给出的结构是否真的匹配。

从「先玩 · 撤销键的真身」走到「程序怎么记得「回到哪」」

「先玩 · 撤销键的真身」先把问题落在「左边是个迷你文档,用四个按钮随便折腾它。 留意右边 :你的每一步操作都被压成一张牌,叠在「操作栈」上—— 后做的压在上面 。折腾够了,连按几下「Cmd+Z 撤销」,看看它撤的顺序」上;到了「程序怎么记得「回到哪」」,讨论继续推进到「你让 AI「做晚饭」,它做到一半要切菜,切菜切到一半刀钝了要磨刀……每次「暂停手头的事去干别的」,程序都会把当前进度压进一个栈里。 看下面的动画 (滚到这里会自动播一遍),留意每个任务完成后,程序是怎么 自动 找回上一个任务的」。两段连起来,重点就不只是记住一个结论,而是看清它成立所依赖的条件。

把这条判断带到下一个场景

遇到一个新的数据结构时,不要从定义开始背。先写出最频繁的操作,再估计数据量和更新方式,最后检查结构是否让这三个条件同时成立。

  • 「先玩 · 撤销键的真身」:左边是个迷你文档,用四个按钮随便折腾它。 留意右边 :你的每一步操作都被压成一张牌,叠在「操作栈」上—— 后做的压在上面 。折腾够了,连按几下「Cmd+Z 撤销」,看看它撤的顺序
  • 「程序怎么记得「回到哪」」:你让 AI「做晚饭」,它做到一半要切菜,切菜切到一半刀钝了要磨刀……每次「暂停手头的事去干别的」,程序都会把当前进度压进一个栈里。 看下面的动画 (滚到这里会自动播一遍),留意每个任务完成后,程序是怎么 自动 找回上一个任务的
  • 「最后的要点」:Agent 的保险丝 :最大步数限制,防的就是任务栈的「无限递归」

最后的「最后的要点」把讨论落到「Agent 的保险丝 :最大步数限制,防的就是任务栈的「无限递归」」。回看这条线索时,最值得保留的是:当输入、规模或风险改变,哪些判断需要重新做一遍。

✅ 这一课想和你分享的

  • 栈 = 一摞盘子:只从顶端放和拿,后进先出(LIFO)
  • 撤销键的真身:每步操作压一张牌,Cmd+Z 永远先弹最近那张
  • 函数调用靠栈记路:弹出栈顶,自动回到上一层暂停的地方
  • 爆栈 = 只压不弹:没有终止条件的递归把栈堆满,程序当场崩溃
  • Agent 的保险丝:最大步数限制,防的就是任务栈的「无限递归」
标记为已学完 阅读进度会自动记录
← 上一篇下一篇 →

继续阅读

同一条线上的下一篇。

文章讨论

读到这里,留下一个判断。

把刚想明白的地方、还没想通的问题,留给下一位一起学习的人。

正在讨论 栈:Cmd+Z 和「爆栈」的秘密 AI 背后的数据结构
3条讨论文章讨论 · 与共学社区同步
在共学社区查看
AM
Asha Morgan内容编辑
观点实践记录

我把这篇文章里的一个判断改写成了今天可以验证的小实验。比记住结论更有用的是,知道下一步要观察什么。

文章讨论7 有帮助
LH
Lin Harper独立开发者
观点观点

读完以后我先回头找它成立的条件,而不是直接把方法搬进项目。这个顺序让后面的取舍清楚很多。

文章讨论5 有帮助
KM
Kiki Moore产品运营
问题问题

如果把这个判断放到真实工作里,最先需要补的约束是什么?我想知道从阅读到第一次实践之间,哪一步最值得先做。

文章讨论4 有帮助