跳转到内容

8. 先猜,再运行,再改一处

约 35 分钟 难度:进阶 动手章

上一章测试全绿,回答的是「这段程序符合哪几条验收条件」。这仍然不等于理解它怎么运转。理解结构是另一件事,需要一套读代码的顺序,但不要求读懂每一行,也不要求记住语法。

这一章就练这套顺序。练习对象是解析函数里最容易出错的一小段:判断价格算不算缺失。

下面这一版判断价格缺失的写法,是练习用的对照版本,不是上一章的最终实现:

对照版本(练习用)
// 判断价格是否缺失
const missingPrice = !price;

AI 给它配了一段解释,听起来很顺:

这里用 !price 判断价格缺失。当价格为 0 时,!0 的结果是 true,所以价格为 0 的行也会被标记为缺失,正好满足第 5 章那条验收条件。

解释流畅,还准确引用了验收条件。先别急着信,也别急着运行,进入第一步。

不运行,只根据这段代码和上面的解释,猜一个具体输入的结果。输入取第 3 章那行价格为 0 的报价:

赠品,个,0

按解释的说法,这一行的 missing 应该是 true。把这个猜测写下来,待会儿用来对照。

现在真跑一遍,把这一行喂进用了对照版本的解析。实际结果:这一行的 missing 是 false,价格照常显示成 0,没有被标成缺失。

实际和猜测对不上。对不上的地方,就是理解出现缺口的地方,也正是解释骗过了直觉的地方。

针对猜错的这一处,加一句临时诊断,看 price 到底是什么:

临时诊断
console.log(typeof price, price);

跑出来是:

string 0

price 是字符串 '0',不是数字 0。而非空字符串在这里算「真」,所以 !'0' 的结果是 false,价格为 0 的行不会被标成缺失。AI 的解释说的是数字 0 的情况(!0 确实是 true),可代码走到这一步时,price 还是分隔出来的字符串。解释本身的语气毫无破绽,却对错了对象。

弄清原因后,只改这一处,把字符串和数值两种情况都判进去:

改一处
// 价格为空,或数值等于 0,都算缺失
const missingPrice = price === '' || price === undefined || Number(price) === 0;

重新跑那一行,missing 变成 true,价格 0 的行被标成缺失,和最初的猜测对上了。改动只落在这一行,其余不动,方便看清楚是这一处带来了变化。这一版也正是上一章最终实现里用的写法。

最后基于刚建立的理解,做一个自己的小扩展。比如加一条规则:价格是负数的行也算异常。把这条规则按第 5 章的方式写成验收条件,交给 AI 补实现和测试,再用刚才这套顺序读一遍它改的那一小段,确认改动只落在价格判断上。

刚才走过的五步,是一套有名字的读码顺序:PRIMM,来自计算机教育研究者 Sue Sentance 等人提出的教学法,五个字母对应五步:

  • Predict(先猜):不运行,先预测输出。
  • Run(再运行):跑一遍,和预测对照。
  • Investigate(探查):针对对不上的地方查清原因。
  • Modify(改一处):只改一小处,验证理解。
  • Make(做一个):基于理解做一个自己的版本。

这是真实的教学术语,不是本书自造的说法。它对不会写代码的人尤其管用,因为每一步的判断都落在「运行看到了什么」上,不落在「能不能读懂整段语法」上。

把刚才读明白的东西画成一张程序结构图:

输入:一段多行文本(每行:品名,单位,含税价)
│
关键处理:
├─ 按行拆分,逗号分隔成三段
├─ 单位为空 → 标记缺失
└─ 价格为空或数值为 0 → 标记缺失 ← 刚才出错又改好的地方
│
输出:每行一个对象 { name, unit, price, missing }
失败位置:价格判断处,字符串 '0' 与数字 0 混淆

这张图记的不是每一行代码,是「数据从哪进、经过哪几处关键判断、从哪出、最容易在哪一处出错」。有了它,下一次这段程序再出问题,能直接指向可疑的那一处,而不必从头重读。

第二幕到这里完整了:从裁决验收条件,到让测试先红,到让 AI 在判定下循环变绿,再到读懂关键的一小段。

这一节有错或讲不清? 提个 Issue 直接改文档 请作者催更