书 · 1999-02 · 布莱恩·W.克尼汉(Brian W. Kernighan)、罗布·派克(Rob Pike)

《程序设计实践》

计算机与 AI书单第 44 名

一句话结论

把程序写出来只是起点:好程序还需要清楚的表达、合适的数据结构、稳定的接口,以及有条理的验证和改进。

值不值得读完整本?

值得读完整本,尤其适合已经会编程、开始维护实际程序的读者。它把容易靠碰壁才学会的工作方法连成一体,具体工具虽有年代感,判断问题的方法仍值得掌握。

  • 先读第1章建立清晰表达的标准,再连读第3、4章,看一个程序和一个库怎样逐步成形;算法基础薄弱时,先补第2章。
  • 第5至7章值得配合自己的程序练习:重现一个错误、补上自动测试,再测量一次真实的性能瓶颈。
  • 第8、9章也应读完,它们分别回答程序怎样适应环境变化,以及怎样借助记法和工具减少手工工作。
  • 已经熟悉基础算法的人可以略读第2章的具体实现;不熟悉书中语言时,可以略过部分语法细节,但应保留数据表示、接口和取舍的讨论。

核心观点

1. 清晰的代码能减少理解和修改中的错误

代码首先要让人读懂。命名、表达式、控制流程和注释应共同表达意图,风格的一致性比争论某一种排版更重要。

为什么:维护程序时,人需要在头脑中还原它的行为。含糊的名字、复杂的表达式和不一致的写法会增加还原所需的推测,也就增加误判的机会。采用语言中熟悉的惯用写法,可以让读者直接识别结构。注释应补充代码没有说明的理由,而不能替难懂的代码承担全部解释工作。

2. 先选好数据表示,再决定怎样实现

算法、数据结构和语言应一起考虑。先弄清程序要保存什么、反复执行什么操作,再选择足够简单而有效的实现。

为什么:数据的组织方式决定哪些操作容易完成、哪些操作需要反复搜索或搬动。表示不合适时,局部代码再精巧也可能反复做多余的工作。语言提供的集合和操作又会改变实现的复杂程度,所以不能脱离语言比较代码长短。理解基础算法的代价,同时善用已有库,才能作出有根据的选择。

3. 用明确的接口和隔离措施控制变化

组件之间应有简单、明确的约定,尤其要说明资源归属和错误处理。依赖具体环境的部分应集中隔离,数据交换也要避免暗中依赖机器表示。

为什么:接口一旦暴露实现细节,调用者就容易依赖这些细节,使一次内部修改牵连许多地方。资源由谁申请、由谁释放如果不明确,各部分即使单独看起来合理,组合后也会出错。类似地,散落在各处的平台假设会使移植变成逐处修补。隐藏内部表示、集中环境差异,可以缩小修改范围,让程序更容易长期维护。

4. 把调试变成调查,把测试变成可重复的工作

调试应从现象和证据出发,逐步缩小错误范围;测试则应边写边做,并尽可能自动重复。

为什么:随意改动程序会同时改变多个条件,使人难以判断究竟什么导致了故障。重现问题、简化输入和检查异常模式,可以减少可能的解释,让每一步调查更有针对性。测试应主动寻找边界和薄弱环节,因为常见输入成功并不能说明这些地方可靠。自动化把已知检查保存下来,使后续修改也能接受同样的检验。

5. 性能改进应由需求和测量引导

先写清楚、正确的程序;确有性能问题时,再测量瓶颈并验证改动效果。优化既包括时间,也包括空间及两者之间的取舍。

为什么:人对耗时位置的直觉容易出错,优化不重要的部分可能几乎不影响整体表现。计时和性能分析能把注意力集中到真正昂贵的工作上。瓶颈有时来自重复计算或不合适的组织方式,改变算法、减少工作比微调语句更有效。优化后仍需重新测量并检查正确性,否则只是用更复杂的代码换来未经证实的收益。

6. 用合适的记法,让机器承担重复劳动

解决问题不一定要继续堆叠通用语言代码。正则表达式、小型专用语言和代码生成,可以让问题表达得更直接。

为什么:重复手写相似逻辑,会把同一规则复制到多个位置,也让遗漏和不一致更容易发生。把规则集中写成一种紧凑记法,再由工具解释或生成代码,可以让修改作用于所有相关位置。记法贴近问题结构时,读者更容易看到规则本身。工具也有实现和维护成本,因此应以实际问题需要为尺度,而不是追求语言或框架的规模。

反对意见

错误报告可以统一采用异常(Doug Hellmann)

Hellmann在《Book Review: The Practice of Programming》中反对把异常留给极少数特殊情况。他认为,把部分错误放在返回值里、部分错误放在异常里,会让调用者维护两套处理通道;统一使用异常还能让无法恢复的底层代码直接向上传递失败。这具体挑战了书中错误处理建议的适用范围。

实践方法还应包括静态检查(Chris Hills)

Hills在ACCU书评中总体高度评价本书,但明确指出它没有讨论lint或静态测试。这项批评提醒读者:依靠运行程序进行测试和调试之外,还应有在运行前检查代码问题的方法;书中的工具图景并不完整。

性能分析结果也需要审查测量方式(Valerie Aurora)

Aurora在《Kernel Hacker's Bookshelf: The Practice of Programming》中支持本书先测量的原则,但认为对性能分析工具的偏差讲得不够。定时采样还是硬件计数、是否计入等待输入输出的时间等差异,都会影响结果。她的补充是:读懂热点列表,还需要了解工具到底测了什么。

测试不能承担证明正确性的任务(Edsger W. Dijkstra)

Dijkstra在《Notes on Structured Programming(EWD249)》中指出,测试只能检查可执行情形中的一部分,不能由此证明所有情形都正确,因此需要利用程序结构和数学推理建立正确性。这是与本书实践重心的对照,并非针对本书的直接反驳;本书也没有把测试等同于证明。

相关推荐

导读由 AI 根据公开资料整理,没有读取原书全文,一切以原著为准。导读 Primer 首页