工程哲学:Donald Knuth,编程是一门艺术

要点速览
- 编程是一门艺术,而不只是工程。 高德纳有意将毕生之作命名为《计算机程序设计艺术》,以一种任何测试套件都无法衡量的美的标准来要求代码。
- 代码是写给人读的文学。 第一读者是下一个阅读程序的人,编译器只是第二位。可读性是第一等的工程要求,而非一种礼貌。
- 只在要紧之处倾注严谨,对其余处则保持克制。 证明算法、精确分析其代价、将工艺打磨到最后一个字符——同时拒绝把精力倾注到那无关紧要的 97% 代码上。
- 「过早优化」意味着先测量。 这句名言是一条测量纪律,而非为慢代码开脱的借口:通过性能剖析找出关键的那几个百分点,只攻克它们,其余部分则保持可读。
原则
「与其想象我们的主要任务是指示计算机做什么,不如把重心放在向人类解释我们希望计算机做什么。」——高德纳1
一句话便涵盖了整个立场。在高德纳看来,程序首先不是指向机器的指令流;它是写给人的文字——写给下一位读者,而那通常就是六个月后、已经忘得一干二净的你自己。编译器是次要读者。第一读者是人,程序要么是写给这位读者的文学,要么就是一次失败,无论它运行得多么正确。
正因如此,高德纳把毕生之作命名为《计算机程序设计艺术》,而非《科学》或《工程》。他所说的艺术是字面意义上的:一门为所造之物的品质而追求的工艺,以一种任何测试套件都无法衡量的美的标准来要求。随之而来的纪律以一种非常具体的方式严苛。它说:去做那少数真正要紧的事,并把它们做到分毫不差。证明你的算法是正确的。精确分析它们的代价,而不是凭猜测。把版面排印得能够毫无阻碍地被阅读。而——人人引用、却几乎人人误读的那部分——不要把你的工艺花在那无关紧要的 97% 代码上,因为把精力倾注到错误的地方并非匠心,而是伪装成勤勉的浪费。
于是这条原则有两副看似对立、实则合一的面孔。在要紧之处处处严谨;在不要紧之处处处克制。难就难在分辨二者——而高德纳对如何分辨的回答,正是贯穿整个系列的那个回答:你去测量。那句关于过早优化的名言并非容许你写慢代码的许可证。它要求你在动手之前先凭证据找出关键的那几个百分点,这与性能是一种你内建并加以测量、而非在最后追加的属性是同一种信念。
背景
唐纳德·欧文·克努斯(Donald Ervin Knuth)于 1938 年 1 月 10 日生于威斯康星州密尔沃基。2他在凯斯理工学院取得学士与硕士学位——凯斯对他印象深刻,以至于在 1960 年将硕士学位与学士学位同时授予——并于 1963 年在加州理工学院获得数学博士学位。2他于 1969 年加入斯坦福大学教职,1977 年成为弗莱彻·琼斯计算机科学讲席教授,并在 1990 年取得一个几乎是他为自己发明的头衔:计算机程序设计艺术教授。2他已担任斯坦福荣休教授多年,至今仍以手写与书信的方式,致力于那个已占据他六十余年的同一项目。
那个项目始于一次范围上的意外。1962 年,Addison-Wesley 委托年轻的高德纳写一本关于编译器的书。3他动笔之后断定,若不先铺设其底层的基础理论——排序、查找、数据结构、算法实际耗时的分析——他便无法诚实地讲清如何构建编译器。一本书于是变成了一套规划中的系列。第一卷《基本算法》于 1968 年问世。计划最终扩展到七卷,而这部著作至今仍未完成。3《美国科学家》后来将它列为塑造了一个世纪科学的约一百本书之一,《纽约时报》则称它为「这一行当的奠基性论著」。3按合理的共识,它是计算机科学最接近一部圣经的东西。
1974 年,高德纳获得 ACM A.M. 图灵奖——计算领域的最高荣誉——「以表彰其在算法分析与程序设计语言设计方面的若干重大贡献,尤其是他通过那一系列著名著作对『计算机程序设计艺术』所作的最重要贡献」。4他的图灵奖演讲一如其风格,题为「作为一门艺术的计算机程序设计」。4

工作
《计算机程序设计艺术》与算法分析
高德纳被称为算法分析之父,这个称号当之无愧。2在 TAOCP 之前,程序员大多以非正式的方式推断算法——这个似乎更快,那个像是更浪费。高德纳的项目是把这种推断置于严格的数学基础之上:精确地推导出一个算法作为其输入的函数所执行的运算次数,并以数学家对待定理那样的精度去做。他普及了如今每位程序员都用来描述代价如何随规模增长的渐近「大 O」记号。2重点从来不在记号;而在于这样一种信念:一个程序的代价是一个可知的量,你应当计算它,而非对它有所感觉。
这些卷册本身以一种近乎偏执的完备性著称——每个论断都经过证明,每个算法都经过分析,每条边界情形都得到处理——也以高德纳为任何被发现的错误所提供的悬赏支票而闻名。支票面额为 2.56 美元,他称之为「一个十六进制美元」,因为 256 美分在十六进制里是 100。5这个金额是个玩笑;其背后的纪律却不是。高德纳是在拿钱打赌他的工作是正确的,并邀请全世界来证明他错了。TAOCP 数十年后依然精雕细琢、依然权威,部分正是这个长期赌注的产物。5(他在 2008 年因银行欺诈停止寄出真实支票,如今改为针对一家想象中的「圣塞里夫银行」开具凭证——严谨延续了下来;纸面凭据则换了形式。)5
过早优化与关键的 3%
软件工程中被引用最多的一句话出自高德纳,而它几乎总是被引错——被剥去了赋予其意义的那个从句。以下是完整段落,出自他 1974 年发表于《ACM 计算综述》的论文「带 go to 语句的结构化程序设计」:
「程序员浪费大量时间去思考、或担忧其程序中非关键部分的速度,而这些追求效率的尝试,在考虑调试与维护时,实际上会产生强烈的负面影响。我们应当忘掉那些细小的效率,约 97% 的时间都该如此:过早优化是万恶之源。然而我们不应错过那关键的 3% 中的机会。」6
整段读来,它并非反对性能的论点。它是支持测量的论点。高德纳的观点是经验性的:一个程序几乎所有的时间都花在其代码极小的一部分里,而优化其余的 97%——那些几乎不运行的部分——要付出真实的精力,几乎换不来什么,还让代码更难阅读、更难信任。所谓「恶」并非优化。恶在于在你知道时间花在哪里之前就去优化——把工艺花在信念上,而非证据上。他所开的处方与懒惰恰恰相反:通过性能剖析找出关键的 3%,然后狠狠地攻克它。
关于归属说一句,因为这对任何诚实引用此句的人都很重要。这句话出现在高德纳 1974 年的论文中,是他自己的文字。但十五年后,高德纳本人却将它称为「霍尔的格言」,归于托尼·霍尔——而当霍尔被直接问及时,他也予以否认,提出它或许出自迪杰斯特拉,最好把它当作「共有的文化或民间传说」来看待。7因此,最干净而准确的说法是:我们大家所引用的措辞是高德纳的,由高德纳于 1974 年发表;而那个想法,他慷慨地拒绝将其据为己有。这种拒绝本身正合其性。那个付钱让你找出他错误的人,对索取他不确定该归于自己的功劳并无兴趣。
TeX、METAFONT 与作为工艺的数字排版
1976 年,高德纳收到第二卷第二版的校样,大为震惊。曾让第一版美观的热金属排版正被早期的照相排版所取代,结果丑陋——尤其是数学公式看起来不对劲。3换作他人或许会把这当作进步的代价而接受。高德纳无法容忍自己的著作被排得难看,于是在 1977 年决定花一点时间做一个更好的东西。那一点时间变成了将近十年。成果便是排版系统 TeX 与设计字体本身的配套系统 METAFONT;两者于 1986 年完成并作为书籍出版。3
他职业生涯中没有哪一段插曲比这更能揭示问题。一位数学家为了从第一性原理出发解决把美丽字符放上版面的问题,把自己的鸿篇巨制中断了八年——因为在他心中,版面的外观与作品的品质是不可分割的。四十年过去,TeX 仍是全世界数学排版的标准。它再清楚不过地表明:对高德纳而言,造物一直延伸到最后一个字形的最后一个像素:工艺并不止步于代码。这是工程师版的把篱笆的背面做好——读者从不会有意识地注意到的那部分,正是他拒绝草草了事的那部分。

文学化编程
如果程序本就是要给人读的,那么我们书写它的方式——按编译器的顺序、把注释作为事后添加之物拼接上去——便是本末倒置的。1984 年,在《计算机杂志》的一篇论文中,高德纳提出将其颠倒过来。他把这种做法称为文学化编程,并构建了一个名为 WEB 的系统来实现它。1
在文学化编程中,你按人类思考的顺序写出一份单一文档——以散文解释你的意图,并在解释需要之处把代码片段织入其中,顺序则以最易读为准。随后由两个工具来处理它:tangle 按编译器所需的顺序抽取代码,weave 生成供人阅读、经过排印的格式化文档。8WEB 结合了 Pascal 与 TeX;后来与西尔维奥·利维合作的 CWEB 则为 C 做了同样的事。8高德纳自己的描述是:「程序最好被设想为一张网……程序员的任务是陈述那些部分以及那些关系,以最利于人类理解的任何顺序。」8
文学化编程从未成为主流实践,这一点值得坦诚以对。但其底下的信念——解释并非对程序的文档,解释本身就是程序;可读性是第一等的工程要求,而非一种礼貌——已悄然取胜。正是这同一种本能,让我们珍视那些自我解释的代码,它也是高德纳对「编程究竟为何」这一问题所给出的最深刻回答。
方法
这套方法在六十年与四个领域中始终如一——算法、排版、语言设计,以及书写本身。
首先为人类读者而写。 机器是第二读者。一个程序,就像一份证明或一个段落,其评判标准在于下一个人能否跟得上。文学化编程便是这一信念化作的工具。18
先证明它,再测试它。 「当心上面代码中的错误;我只证明了它正确,并未试运行过。」高德纳在 1977 年的一份备忘录中写道——这句话之所以好笑,恰恰因为他是那种真会去证明自己代码的稀有之人。9这个玩笑里包含着纪律:正确性是论证出来的,而非假定的,而一个证明虽是必要的,却仍不充分。两者你都欠着。
分析代价,不要猜测它。 一个算法的运行时间是一个要用数学推导出来的量,而非一种直觉。这是算法分析的奠基之举,也是为何「大 O」挂在每位程序员嘴边。2
先测量,再优化。 过早优化是万恶之源,因为它把工艺花在信念上。凭证据找出关键的 3%;只在那里优化;其余部分保持可读。6
让工艺贯彻到最后一个字形。 为构建 TeX 而把毕生之作停下八年,是这套方法最纯粹的体现:造物的品质并不止于代码止步之处。它终于版面的边缘。3
守护深度专注。 高德纳于 1990 年 1 月 1 日放弃了电子邮件——「自那以后我一直是个快乐的人」,他写道——其理由是,电子邮件适合那些以掌握全局为职责的人,而他的职责是处在事情的底层,把一门学问钻研得足够深,以便讲解得当。10拒绝打扰并非古怪;它是这项工作的前提。
影响链
谁塑造了他
数学传统,尤其是组合数学与数论。 高德纳是以数学家的身份走向编程的,他把数学家那不可让步的标准——一个论断在被证明之前不算知识——带进了一个一直靠直觉勉力维持的领域。(塑造性影响)
Addison-Wesley 1962 年的委托。 那个奠定其轨迹的意外。受邀写一本关于编译器的书,高德纳却顺着问题一路追到其根基,再没回头;算法分析之所以以其现代形态存在,正因为他拒绝去写那本人家请他写的简单书。(直接影响)
热金属排版与印刷书籍。 他对一个排印版面应有的样子所持的标准,是由更早一个印刷时代的工艺所塑成的——这正是为何 1976 年那些照相排版的校样令人无法忍受,而 TeX 变得必要。(塑造性影响)
他塑造了谁
每一位在职的程序员。 大 O 分析、对算法的严格研究以及 TAOCP 本身,都是基本素养。一代人从高德纳那里学到了一个算法的代价,无论他们是否真的翻开过他的书。
全世界的数学与科学出版。 TeX 与 LaTeX 是过去四十年里几乎每一篇数学论文、物理预印本与技术书籍排印所依托的基底。高德纳那八年的绕道成了基础设施。
正确性与可读性的文化。 文学化编程、悬赏支票、「只证明了它正确,并未试运行过」——这些确立了一种基调。认真的代码应当既可被证明为正确、又读来令人愉悦,这一期望在某种程度上是高德纳的遗产。
贯穿线
高德纳与艾兹格·迪杰斯特拉是本系列中两位伟大的正确性使徒,而他们之间富有成效的张力,是其中最具启发性的东西。两人都以手写作,都不信任机器作为真理的裁决者,都坚持程序应当被推理,而非仅仅被运行。但迪杰斯特拉认为测试只能显示错误的存在、永远不能显示其不存在——并强烈倾向于把证明当作唯一真正的根基——而高德纳那句「我只证明了它正确,并未试运行过」却对证明本身的局限会心一笑:他既证明又测试,并在两者都失手时付钱给你。严谨之中的务实。约翰·卡马克处在同一坐标轴的另一端——剖析热点、把机器理解到根基、攻克关键循环——这恰是高德纳的「关键 3%」,只不过瞄准的是硬件天花板,而非证明。而汤普森与里奇的 Unix——其中 C 的设计是为了让一个操作系统能够被阅读与移植,而非手工焊死在一台机器上——正是高德纳所道明的同一信念:程序是写给人的。(系列之桥)
我从中汲取的
我所守住的教训是:我这一行被引用最多的那句话,是一条披着格言外衣的测量纪律。「过早优化是万恶之源」被用来为又慢又懒的代码开脱,而这恰是高德纳本意的精确反面。他真正要求的,比「全部优化」或「什么都不优化」都更难:先剖析,凭证据定位关键的 3%,把你的工艺倾注到恰恰那里,其余部分则保持可读。这与质量是唯一的变量是同一标准——问题从来不是「这够快了吗?」或「这够聪明了吗?」,而是「我有证据知道代价究竟在哪里吗?」这是证据之门应用于性能:对热点的一种直觉,在你测量它之前,并不算知识。
在我如今所构建的世界里——智能体、工具循环、模型调用——高德纳的两副面孔都承重。克制的一面:不要去优化一条你没有测量过的提示或检索路径;几乎所有的延迟都活在少数几次调用里,其余则是你应当保持可读的噪声。严谨的一面:在要紧之处,证明它、测试它,并押上一些你认为自己正确的东西。而文学化编程的那个信念,是我在五十年之后觉得最切题的一个——当一个系统能以快过任何人阅读的速度生成代码时,首先为人类读者而写便不再是一种讲究,而成了唯一让系统得以被审视的东西。那个信念——即品味是一个你加以测量并捍卫的技术系统,而非一种你声称的感觉——从 1962 年的一次约稿一路贯穿到 2026 年的一套智能体框架。
常见问题
高德纳的工程哲学是什么?
高德纳把编程视作一门艺术,其第一读者是人,而非机器:「向人类解释我们希望计算机做什么。」1随之而来的纪律把严苛的严谨与刻意的克制配成一对——证明算法正确、用数学分析其代价、把工艺贯彻到排印好的版面,同时拒绝把精力花在优化程序中那些不要紧的部分上。他靠测量来分辨二者,这正是他那句关于过早优化的话的真正含义。6
「过早优化是万恶之源」究竟意味着什么?
它是对测量的要求,而非对性能的否定。1974 年的完整引文是:「我们应当忘掉那些细小的效率,约 97% 的时间都该如此:过早优化是万恶之源。然而我们不应错过那关键的 3% 中的机会。」6高德纳的观点是,一个程序几乎所有的时间都花在其代码的一小部分里;优化其余的 97% 是浪费精力、损害可读性。纪律在于先剖析,找出关键的那几个百分点,只优化它们。用这句话为慢代码开脱,是把它的意思颠倒了过来。
「过早优化」这句话是高德纳发明的,还是霍尔?
人人引用的措辞是高德纳的,发表于他自己 1974 年那篇《计算综述》论文「带 go to 语句的结构化程序设计」。6然而十五年后,高德纳本人却将它称为「霍尔的格言」并归于托尼·霍尔;而当霍尔被直接问及时,他也否认了著作权,提出它或许可追溯到迪杰斯特拉,并说最好把它当作「共有的文化或民间传说」。7诚实的总结是:我们所引用的这句话是高德纳的,但他慷慨地拒绝把这个想法据为己有。
高德纳创造了什么?
高德纳撰写了《计算机程序设计艺术》(始于 1962 年,第一卷 1968 年,仍在进行中——计算机科学的奠基性论著),并创立了现代的算法分析,普及了大 O 记号。23因不满自己著作的糟糕排版,他创造了 TeX 排版系统与 METAFONT 字体系统,两者均于 1986 年出版,至今仍是数学出版的标准。3他还开创了文学化编程与 WEB 系统,即把程序写来供人阅读的实践。18他获得了 1974 年的 ACM 图灵奖。4
来源
-
Donald E. Knuth,「Literate Programming,”《The Computer Journal》27(1984)。「Instead of imagining that our main task is to instruct a computer what to do, let us concentrate rather on explaining to human beings what we want a computer to do.」另见「Literate programming,” Wikipedia。 ↩↩↩↩↩
-
「Donald Knuth,” Wikipedia。1938 年 1 月 10 日生于威斯康星州密尔沃基;凯斯理工学院(学士/硕士 1960);加州理工学院数学博士(1963);斯坦福教职(据 Wikipedia 为 1969 年——某些传记来源给出 1968 年,即《基本算法》出版之年,故常有混淆),弗莱彻·琼斯计算机科学讲席教授(1977),计算机程序设计艺术教授(1990),现为荣休;「算法分析之父」;普及了渐近(大 O)记号。 ↩↩↩↩↩↩↩
-
「The Art of Computer Programming,” Wikipedia。Addison-Wesley 于 1962 年委托一本编译器书;项目扩展为规划中的七卷;第一卷于 1968 年出版,著作仍在进行中;被《American Scientist》列入塑造了一个世纪科学的书籍之列,并被《纽约时报》称为「这一行当的奠基性论著」;1976 年照相排版之挫折促使高德纳在 1977 年着手 TeX,八年后才回归;TeX 与 METAFONT 于 1986 年作为书籍出版;面额 2.56 美元的「一个十六进制美元」悬赏支票。 ↩↩↩↩↩↩↩↩
-
「Donald E. Knuth – A.M. Turing Award Laureate,” ACM(官方页面屏蔽自动化请求;参见下方可供爬虫访问的第一手来源镜像)。1974 年颁奖词逐字(在典礼上宣读并随演讲重印):「The 1974 A.M. Turing Award is presented to Professor Donald E. Knuth of Stanford University for a number of major contributions to the analysis of algorithms and the design of programming languages, and in particular for his most significant contributions to the ‘art of computer programming’ through his series of well-known books.」图灵奖演讲:Donald E. Knuth,「Computer Programming as an Art,”《Communications of the ACM》17, no. 12(1974 年 12 月):667–673(全文,其中亦重印了颁奖词)。 ↩↩↩
-
「The Art of Computer Programming,” Wikipedia,以及「Knuth reward check,” Wikipedia。每发现一处错误即奖励价值「一个十六进制美元」(256 美分 = 2.56 美元)的支票;真实支票于 2008 年因银行欺诈停止发放,改为针对想象中的「圣塞里夫银行」开具的凭证。 ↩↩↩
-
Donald E. Knuth,「Structured Programming with go to Statements,”《ACM Computing Surveys》6, no. 4(1974 年 12 月):261–301。「Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs… We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%.」论文引用亦见于「Structured programming,” Wikipedia。 ↩↩↩↩↩
-
关于归属:高德纳在其 1974 年论文约十五年后将这句话称为「霍尔的格言」,而托尼·霍尔被直接问及时予以否认,提出迪杰斯特拉以及「共有的文化或民间传说」。参见「Programming Myths and Folklore: The Origin of ‘Premature optimization is the root of all evil’,” 中记录的讨论与霍尔的回应,以及「Donald Knuth,” Wikiquote 上的归属注记。 ↩↩
-
「Literate programming,” Wikipedia。高德纳 1984 年的《Computer Journal》论文;WEB 系统(Pascal + TeX)与后来的 CWEB(与西尔维奥·利维合作,为 C);tangle 抽取可编译的代码,weave 生成排印好的文档;「a program is best thought of as a web… the programmer’s task is to state those parts and those relationships, in whatever order is best for human comprehension.」 ↩↩↩↩↩
-
Donald E. Knuth,「Notes on the van Emde Boas construction of priority deques: An instructive use of recursion」(备忘录,1977),如「Donald Knuth,” Wikiquote 所记录。「Beware of bugs in the above code; I have only proved it correct, not tried it.」 ↩
-
Donald E. Knuth,「Knuth versus Email,” 斯坦福主页。「I have been a happy man ever since January 1, 1990, when I no longer had an email address.」电子邮件适合那些以掌握全局为职责的人;高德纳的职责是处在事情的底层,在讲解之前先深入钻研一门学问。 ↩