4 Structured Programming

埃兹格・维贝・迪杰斯特拉 1930 年出生于鹿特丹。他在二战中躲过了鹿特丹大轰炸和德国对荷兰的占领。1948 年,他以数学、物理、化学、生物全满分的成绩高中毕业。
1952 年 3 月,21 岁的迪杰斯特拉进入阿姆斯特丹数学中心工作,成为荷兰第一位程序员。
1955 年,当了三年程序员、仍在读大学的他得出一个结论:编程带来的智力挑战,比理论物理更大。于是他选择把编程作为终身职业。
1957 年,迪杰斯特拉结婚。当时荷兰在婚礼登记时必须填写职业。官方不接受 “程序员” 这个职业 —— 他们从没听过。为了登记,迪杰斯特拉只能填 “理论物理学家”。
在决定投身编程时,他和上司阿德里安・范・维恩加登聊过。他担心:编程还没有形成一门学科或科学,自己不会被认真对待。上司回答:你很可能就是那个能建立这门学科的人。
迪杰斯特拉的职业生涯始于真空管时代。那时计算机巨大、脆弱、缓慢、不可靠,程序只能用二进制或粗糙的汇编写。输入靠纸带或打孔卡,编辑 — 编译 — 测试的循环动辄几小时甚至几天。
就是在这样原始的环境里,迪杰斯特拉做出了伟大发现。
证明
迪杰斯特拉很早就意识到:编程很难,程序员很容易出错。任何复杂一点的程序,细节多到人脑无法完全掌控。漏掉一个小细节,程序就可能看似正常,却在奇怪的地方崩溃。
他的解决方案是:引入数学意义上的 “证明”。他的理想是建立一套像欧几里得几何那样的公理、定理、推论体系。程序员可以像数学家一样,使用已被证明的结构,再把它们组合起来,并自己证明代码正确。
要实现这一点,他必须先证明简单算法是正确的。在研究中他发现:某些 goto 用法,会让模块无法被递归拆分成更小单元,
导致无法用分治法去证明程序正确。
但另一些 goto 用法没问题 ——它们对应我们熟悉的 if/then/else 和 do/while。只用这类控制结构的模块,可以被递归拆分成可证明的单元。
迪杰斯特拉知道,这些结构很特殊。两年前,Böhm 和 Jacopini 已经证明:所有程序都只用三种结构就能构造:顺序、分支、循环。
这个发现意义重大:能让程序可被证明的控制结构,
恰好就是构造所有程序的最小必需结构。
于是,结构化编程诞生了。
迪杰斯特拉证明:
顺序语句:可以通过逐一枚举输入输出证明正确。
分支语句:枚举每条路径,只要都正确,整个分支就正确。
循环语句:要用数学归纳法,证明初始条件、递推步骤、终止条件都正确。
这些证明繁琐复杂,但它们是证明。迪杰斯特拉的欧几里得式定理体系,看起来触手可及。
一篇引发轩然大波的文章
1968 年,迪杰斯特拉给《CACM》写了一封信,3 月刊发。标题是:《Go To 语句有害论》
文章一发表,整个编程界炸了。那时候没有互联网,大家没法网上对线,但他们疯狂给各大期刊写信。有人猛烈抨击,有人强烈支持。这场论战持续了近十年。
最终结果很简单:迪杰斯特拉赢了。
随着语言演进,goto 逐渐被边缘化,几乎消失。如今大多数现代语言根本没有 goto,LISP 更是从来没有。
我们现在全都是结构化程序员,不是因为我们选择,而是语言已经不让我们随便乱跳转了。
功能分解
结构化编程让模块可以递归分解为可证明的小单元。也就是说:你可以把一个大问题拆成高层函数,每个函数再拆成更小的函数,无限拆下去。
这就是 功能分解(functional decomposition)。以此为基础,结构化分析、结构化设计在 70 年代末到 80 年代大行其道。程序员可以把巨大的系统拆成模块、组件、 tiny function。
但形式化证明并没有到来
然而,迪杰斯特拉梦想的形式化证明最终没有普及。没有程序员会无聊到给每个小函数写数学证明。今天几乎没人认为这是做高质量软件的合理方式。
但数学证明不是验证正确性的唯一方法。另一个更成功的方法是:科学方法。
科学来救场
数学和科学的根本区别:
数学:证明命题为真。
科学:无法证明理论绝对正确,只能证伪。
比如牛顿定律,你只能做无数实验验证,但永远无法用数学绝对证明它永远正确。科学的本质是:无法被证伪的,我们就暂时当它是对的。
测试
迪杰斯特拉有句名言:“测试只能证明存在 bug,不能证明没有 bug。”
一次测试失败,可以证明程序错误。但再多测试成功,也不能数学上证明程序绝对正确。只能说:我们尽力了,它足够正确,可以用。
这意味着:软件开发不是数学,而是更像科学。
而只有可被证明的程序(结构良好、没有乱跳转),才能被有效测试和证伪。如果代码乱得一塌糊涂,测试再多也没用。
结构化编程强迫我们:把程序拆成小的、可证伪的函数。我们用测试去尝试证明它错。如果证明不了,我们就认为它够用了。
结论
结构化编程今天依然有价值,正是因为它能让我们写出可证伪、可测试的代码单元。
这也是现代语言去掉无限制 goto 的原因。在架构层面,功能分解依然是最佳实践之一。
从最小的函数到最大的组件,软件都像一门科学,以可证伪性驱动。架构师的目标,就是设计出容易测试、容易证伪的模块。他们用的就是类似结构化编程的约束性纪律,只是在更高层面。
这些约束性纪律,就是我们后面章节要详细学习的内容。

posted @ 2026-03-19 15:55  cyusouyiku  阅读(14)  评论(0)    收藏  举报