注:本文为 "A Discipline of Programming" 相关合辑。
略作重排,如有内容异常,请看原文。
《编程的修炼》译者序
E.W.Dijkstra 原著,裘宗燕译(电子工业出版社,2013.7)
图灵奖获得者 Edsqer W. Dijkstra(1930.5.11-2002.8.6)是每个在计算机领域中学习和工作的人都应该了解和尊重的先驱者,其思想和技术遗产散布在计算机科学技术的众多领域。刚开始学习编程的人们就应该接触到结构化编程的基本思想,在专业学习的早期,还会遇到查找图中最短路径的 Dijkstra 算法。如果学习并发程序技术,那里更是随处散布着 Dijkstra 的思想和技术贡献。
Dijkstra 是 20 世纪 70 年代前后程序理论研究领域最重要的开拓者之一,也是那个时代的技术英雄。1968 年 Dijkstra 给 CACM 的一封信引起轩然大波,激起了结构化编程的倡导者们与维护 goto 的保守人士的大辩论,最终成就了结构化编程的革命,这是软件开发从个人技艺走向技术和科学的伟大的第一步。Dijkstra 与 Tony Hoare 和 Ole-Johan Dahl 的专著 Structured Programming 以其三位作者分别获得图灵奖而永远不可能被超越。其中 Dijkstra 撰写的"Notes on Structured Programming"提出了软件设计的许多指导原则,其深刻影响覆盖了从基础计算机教育直到复杂软件系统设计的广泛领域。Dijkstra 的另一重大贡献是作为最主要的奠基人,参与了并发程序理论和技术基础的开发。他基于自己的并发编程实践提出了许多概念,开发了许多技术。这个领域里的许多术语出自他的思考,包括互斥、同步、竞态条件、死锁、非确定性、自稳定等。他提出了信号量、P/V 操作、临界区等概念,是今天所有并发语言和机制的基础。他还留给我们许多并发程序范例,如读者/写者问题、生产者/消费者问题、就餐的哲学家问题等,其中许多就是他上课时留给学生的练习。他开发的 THE 多道程序系统为操作系统的理论和实现竖起了第一根标杆。Dijkstra 在程序正确性领域也做出了极其重要的贡献,这是 20 世纪 70 年代之后他最关注的领域,本书就是他一段工作的最重要总结,将在下面详细讨论。如果希望了解 Dijkstra 的更多贡献,可以参考介绍他的 WiKi 页和纪念网页。
本书原名为 A Discipline of Programming,是 Dijkstra 有关程序理论和编程技术的最重要著作。本书是一本非常独特的编程技术专著。
首先,常见的编程教科书通常要选一种流行语言作为示例的载体,而本书却没用任何可以由计算机执行的语言,所有示例程序都没运行过,但它们的正确性却有保证。作者也没采用"伪代码",因为伪代码会带来不可避免的歧义性,也无法作为严格的形式化推理的基础。书中的程序采用作者自己设计的一种小语言描述,该语言反映了顺序(非并行)编程语言的最本质性质,还有许多重要特性将在下面讨论。
其次,书中给出了一大批编程实例,采用的方式与其他书籍大相径庭。作者在给出要解决问题的描述之后,总是非常详尽地展示了从问题出发,通过细致的分析思考,逐步深入工作,最后做出所需程序的整个过程。通过这种过程,作者反复展示了在编程中应该怎样思考问题,提出合适的概念,规划处理流程,通过自上而下的设计和开发逐步做出程序框架,分析、解决遇到的具体问题,填充各部分细节,直至做出完整的程序。作者特别提出了"关注点分离"的重要性,强调在一个阶段解决一个问题。在一些复杂程序的开发完成后,还给出了细致的回顾性分析。作者在书中特别注意开发过程的真实性,并不回避提出过的错误概念和走过的弯路。在其他编程著作中,很难看到这种真实的实践。
虽然上面两点已明显表现出本书的与众不同,但它们还不是本书最重要的特点。Dijsktra 在本书中最重要的关注点是程序正确性,是关于程序及其意义的"数学推导"和"证明"。作者在前言中写到,我们的目标应该是"做出高度可信的程序,而不仅是......一些胡乱涂抹出来的,......,随时准备被第一个反例推翻的程序文本"。他认为编程工作的目标应该是直接得到正确程序,而不是得到一些"大致能用"的代码。令人遗憾的是,目前的编程工作都并不如作者所愿,人们在开发中反复测试开发的半成品或成品,找出其中缺陷并予以更正,直至其作品或工作过程满足了某些外在"评价标准",或由于时间、金钱、人力限制而不得不强行结束。到了工作完成时,开发者们也不敢断定给出的就是所需要的"那个"程序。
有关如何开发出正确的程序,书中强调了许多方面的问题。首先是大家熟悉的问题分析,需要在着手编程前把问题弄清楚,做出严格定义,这件事怎么强调都不过分。这件事不需要过多讨论,书中有许多具体示例可供参考。更重要的是,Dijkstra 认为保证程序正确的主要武器是数学意义上的严格描述和推导。他在本书中给出了一大批示例,展示了严格推导在编程领域里的威力。Dijkstra 认为程序语言(无论哪种语言)本身就是一种形式化的严格定义的描述形式,用它们写出的程序具有确切的语义。用具体计算机运行程序,就是实现其语义,但程序的语义并不依赖于运行它的计算机,可以独立地考虑和表达。从这种认识出发,Dijkstra 基于状态和断言的概念提出了最弱前条件的思想,用谓词转换器作为程序语义的落脚点。由于常规语言不能完美地支持这种想法,他自己开发了一种语言,其中最重要的新概念是卫式命令,选择和重复结构都基于卫式命令定义。Dijkstra 用实例展示了通过严格推导,直接开发出正确程序的过程,展示了不变式、最弱前条件、谓词转换器等重要概念和严格的逻辑推理怎样在程序开发中发挥作用,帮助开发者(这里的实践者是 Dijkstra 本人)理清程序的线索,逐步推导出正确的程序。作者提出的最弱前条件、谓词转换器、非确定性等概念,对程序理论和实践的发展都产生了重要影响。
当然,并不是每个人都赞同 Dijkstra 的想法,即使在未来世界中,也未必所有程序都采用他的理想主义的方式开发。对于数学和逻辑究竟能在编程世界里起多大作用,怀疑者们还在不断地提出质疑,而信仰此道的研究者们也一直在努力工作。可以看到,有关领域的研究取得了许多成果,这些成果正在不断被纳入越来越多的重要软件公司的开发过程,变成流行软件工具的有机组成部分。另一方面,在流行的专业编程书籍中,我们也越来越多地看到有关状态、断言、前后条件、不变式、基于协约的编程等内容的讨论。这些趋势说明,编程作为一种复杂的智力劳动,由于其工作成果将变成当前和未来的工业化、信息化社会中所有基础设施中最关键的支柱,人们必将越来越强烈地关注其产品的正确性和可信任。本书作者在三十多年前的呼喊并没有过时,也不会被遗忘,他提出的想法和技术在未来软件开发中必定会起到越来越重要的作用。原因无二,正如作者在另一处所言:"我早已对计算领域的实践活动有个总结:在那里乱搞的自由度太大,因此,数学的优美绝不是可有可无的奢侈,它关系到生命和死亡"。
本书出版于 1976 年,20 世纪 80 年代曾被影印并在国内计算机科学界流传,当时书名被译为《程序设计训练》,但很奇怪的是,这本书没有出版过中文译本。20 世纪 90 年代后期,国内计算机领域开始迅猛发展,而这一经典却销声匿迹不为人知,确实很令人遗憾。这次电子工业出版社引进本书,是做了一件绝大的好事。我接手时出版社曾计划以《编程的法则》为书名,我对此有疑。Dijkstra 在本书最后专门讨论了一个科学(或其他)领域的形成,及其参与者的智力活动的情况和性质。本书序言作者(图灵奖获得者 Tony Hoare)将编程与音乐、诗词等领域相提并论,讨论一个领域中的卓越成就者对后人的启迪和引领。作为一种智力活动的学习和实践者,即需要获取他人经验,也需要内省和自我总结,把这种获得说成是学习一些"法则",当然不妥。作为领域专家的 Dijkstra 虽然锋芒毕露,但其实也是一个谦虚的人。原书名用不定冠词"A"开头,表示这只是他认为的在编程领域里有道理的一套智力修炼方法,未必就是终极和唯一的"那一套"。由于以《编程的一种修炼》作为中文书名很别扭,我选了目前这个名字,还好,它也没有强加于人的意味。
翻译本书,就像是在倾听作者缓缓道出的深邃思考,非常有趣,不断地受到启发(虽然我原来已经看过几遍),但也非常累。想把这本书翻译好,确实很困难,术语是最简单的问题(但也颇费琢磨),更重要的是既要准确反映原文的字面意思,还要尽可能反映作者的意图和想法。在此基础上还要尽量符合汉语的习惯,使语言流畅容易理解。要把本书译到满意,我觉得至少还需要一两年时间。但是出版社不能等,因此,我只能把目前的版本呈献给读者。我当然要对书中所有的缺陷负责,并对它们的存在表示歉意。我在这里为本书建一个专门页面,做些后续的弥补工作。最后我要衷心感谢出版社的编辑,他们通过认真工作找出了译文里的一些错误。
裘宗燕
北京大学数学学院信息科学系
2016.11
A Discipline of Programming 阅读评述
圆角骑士魔理沙 发布时间:2016‑05‑11 21:36
一、阅读概况
本文为对书籍 A Discipline of Programming 的阅读总结与评价。作者完成全书主体阅读,其中倒数第三、四章内容未进行完整研读。
二、书籍内容定位与认知偏差
初读时,该书易产生认知偏差。作为算法相关著作,其仅包含十余种算法,且大量篇幅用于定义专用程序设计语言,文本表述严谨抽象,与常规算法教材呈现形式存在显著差异。
上述认知仅停留在表面。该书与 Software Foundations 具有相似特征:后者以 Coq 为载体阐述程序设计语言理论,而 A Discipline of Programming 以算法为载体,系统阐述程序设计语言原理与程序构造方法。若仅以学习具体算法为目标,阅读预期难以满足;若关注程序设计思想与推理方法,则该书具有较高参考价值。
完成阅读后,可加深对 Dijkstra 相关论述(如 The Humble Programmer 及 EWD 系列文稿)的理解,重构对计算过程(computation)的认知,建立程序设计的推理与演绎范式,并体会严格形式化推理(radical reasoning)的体系。该书不侧重算法知识的传授,其价值体现在程序设计方法论层面。
三、专用程序设计语言特征
该书前半部分定义的专用程序设计语言具有显著特点。该语言以 不确定性结构(nondeterminism construct) 为基础,形式上与 while 语言相近,但其设计思想具有冲击性。
形式化与极简主义贯穿语言设计。该体系对常规 if 结构提出反思,指出其在程序推理中存在的局限。同时,变量(variable)机制经过严谨设计,初读时易被判定为冗余设计,在反复研读后可体现其在形式推理中的必要性。该语言设计优于部分同期图灵奖获得者著作中定义的专用语言。
四、基于形式化方法的问题求解(该书后半部分内容)
该书后半部分以前半部分定义的专用程序设计语言为工具,对等价类(equivalence class)、最短生成树(shortest spanning tree)等典型问题进行求解,这也是该书后半部分的核心内容呈现。
内容呈现遵循严格形式化流程,完整保留证明(proof)结构。在掌握基础推理范式后,部分形式化证明作为练习(exercise)省略,重点转向问题求解思路的拆解与引导,具体包含以下几类关键方法:
-
对中间步骤引入合理假设(assumption),如在最短生成树问题中,假设已选定的边集本身构成局部最短生成树,这一思路明确了归纳假设强化的适用场景与核心逻辑;
-
利用程序已确定的约束条件进行优化,例如将朴素算法由三趟扫描(3 pass)简化为两趟扫描(2 pass),最终推导得出 Rem 算法,体现了基于现有逻辑优化程序效率的推理思路;
-
将循环体内的计算逻辑移出,采用增量更新(incrementally update)策略,通过调整计算流程,提升程序的简洁性与可推导性。
五、问题分析与分解及程序设计的推理与演绎范式
全书以问题分析、分解与求解为主线。程序设计语言的构造遵循该逻辑,后续算法设计同样遵循该逻辑。阅读过程可理解为与 Dijkstra 共同完成问题求解:
-
从问题定义出发,通过启发式思路提取关键认知(insight)并完成问题分解,对应算法设计的核心流程;
-
从现有方案出发,对已有结构进行质疑、检验与精简,对应专用程序设计语言的构造逻辑。
结合全书内容,Dijkstra 提出的程序设计推理与演绎范式主要包括以下几类,贯穿于语言设计与问题求解的全过程:
-
形式化推理范式:以严格的形式化语言为载体,对程序的每一步逻辑进行严谨证明,确保程序逻辑的正确性,避免模糊性推理,这一范式在该书前半部分语言定义和后半部分初始问题求解中体现尤为明显;
-
启发式演绎范式:通过直觉与启发式思路(heuristic)提取问题关键信息,基于已有认知推导问题解决方案,例如在最短生成树求解中,通过假设局部最优推导全局最优的演绎逻辑;
-
增量优化范式:在程序设计与问题求解中,不追求一步到位的完美方案,而是通过逐步调整、精简与优化,逐步完善程序逻辑,如将朴素算法多趟扫描优化为少趟扫描、将循环内计算改为增量更新等;
-
假设强化范式:在归纳推理过程中,通过合理强化归纳假设,突破原有推理局限,简化问题求解难度,例如在最短生成树问题中,通过强化已选边集的局部最优假设,顺利完成全局最优的推导;
-
问题分解范式:借助关注点分离(separation of concern)等方法,将复杂问题拆解为若干个可认知、可处理的独立单元,降低问题求解的复杂度,使复杂问题变得 intellectually manageable。
Dijkstra 在书中提出:单一部著作无法直接建立完整的思维体系,但可借助上述推理与演绎范式,尤其是关注点分离方法,将复杂问题分解为可认知、可处理的单元。关注点分离与结对编程(pair programming)均为软件工程领域的重要实践。
六、文本特征与阅读建议
该书文本表述严谨抽象,阅读门槛较高。该特征源于作者对复杂问题的认知:困难内容的本质无法通过表述修饰简化。英文阅读能力有限的读者,建议选择中译本辅助理解。
中译本阅读反馈
知乎用户 DMHN39 2016-06-20
阅读裘宗燕教授翻译的《编程的修炼》(中英双语版)至第 18 章,形成如下理解:
-
程序设计中可减少 else 结构,条件判断应满足互斥性,降低分支对上下文的依赖;
-
非预期条件触发时,应采用异常终止机制;
-
程序构造应保证循环条件严谨,不应弱化循环条件以换取近似正确的结果,宁可出现无限循环并进行修正。
书中算法设计体系具有较高深度,脱离文本后独立复现与应用存在难度。
- 《编程的修炼》
https://www.math.pku.edu.cn/teachers/qiuzy/books/dop/translation.htm - A Discipline of Programming看完了 - 知乎
https://zhuanlan.zhihu.com/p/20885133