跳轉到

8.3 戴克斯特拉(Dijkstra)與「GOTO 害人論」:結構化控制流的勝利#

1968 年:一封兩頁的信改寫了行業#

Edsger W. Dijkstra(1930–2002,荷蘭)1968 年 3 月在 ACM 的 Communications 上發表了程式設計史上最著名的書信:〈GOTO 語句有害論〉(標題是主編的編輯成果,Dijkstra 原題更溫和)。

核心論證只用一段話:

我們的智力能力在處理「非靜態關係」時最為吃力。而 GOTO 的罪惡,正在於它使程式的執行位置無法被靜態文本所刻畫——變數的值在變化,而程式的位置也在跳躍,兩者的組合超出了人腦的分析能力。

翻譯成現代語言:「程式碼的文本結構應當與執行結構同構」。while 迴圈的文本邊界就是執行邊界;GOTO 則讓執行流像義大利麵——閱讀成本隨規模超線性增長。

論證的理論後盾:Böhm–Jacopini 定理(1966)#

結構化運動不是純修辭。1966 年 Böhm 與 Jacopini 證明:

任何可用 GOTO 表達的流程圖,都可以用三種結構——順序(sequence)、選擇(if)、迴圈(while)——組合表達。

即:結構化控制流在表達力上無損失。理論保證了可行性,Dijkstra 提供了品味與論證,二者合力完成了範式轉移。「先證明可以不失去什麼,再號召放棄它」——這是技術變革論證的黃金結構。

Dijkstra 其人:一位「反直覺」的程式設計哲學家#

Dijkstra 的貢獻遠超 GOTO 之戰(最短路徑演算法 1959、信號量與並行原語 1965、哲學家進餐問題、自穩定系統)。他的風格是程式設計史獨特的風景:

  • 手寫 EWD 筆記(終身以鋼筆書寫,拒用電腦寫作!),編號 EWD1 至 EWD1318——一個人的思想期刊成為學科經典
  • 名言製造機:「測試只能證明錯誤的存在,不能證明錯誤的不存在」;「電腦科學和電腦的關係,不大於天文學和望遠鏡的關係」
  • 他的立場源於數學家的驕傲:程式應當像定理一樣可證、可推演——正確性靠設計而非除錯。這條路線後來成為形式化方法(Hoare 邏輯、SPARK、TLA+)的靈魂

「軟體危機」與 NATO 會議(1968):GOTO 之戰的制度背景#

GOTO 之戰不是孤立事件。1968 年秋,北大西洋公約組織在德國召開軟體工程會議——第一次把「軟體專案普遍超支、延期、失控」命名為危機。背景:

  • 1960s 大型專案(IBM OS/360——布魯克斯《人月神話》1975 的素材庫)屢屢失控
  • 硬體效能每兩年翻倍,軟體規模爆炸但人類的方法仍是手工藝
  • 會議的藥方:結構化設計、模組化——「軟體工程」一詞誕生

GOTO 廢除 = 軟體危機的第一劑藥。之後的藥方依序登場:模組化(Parnas 1972 資訊隱藏)、物件導向(10–11 章)、敏捷(14.1)、DevOps、乃至今天的 AI 輔助——六十年來的軟體工程史,就是「馴服複雜性」的工具迭代史。

結構化程式設計的勝利路徑#

年份 事件
1966 Böhm–Jacopini 定理
1968 Dijkstra 書信 + NATO 會議
1971 Wirth 的「逐步精化」方法論
1972 Parnas〈模組化準則〉(資訊隱藏)
1975 布魯克斯《人月神話》——危機的病理報告
1976 McCabe 圈複雜度(用數字執行 GOTO 之戰)
1980s 結構化成為行業標準

有趣的最終形態:GOTO 沒有被語言層面禁止(C 至今保留),而是被文化與工具邊緣化(程式碼審查、靜態分析、圈複雜度指標)。範式轉移的最終載體不是語法,是社群的品味。

教育史的一課#

1970s–80s,結構化程式設計成為全球大學課綱(Pascal 是載體);BASIC 的 GOTO 教學法(Dijkstra 曾毒舌「受過 BASIC 訓練的學生在智力上不可挽回」)被逐步淘汰。這場教育的連鎖反應:

  • 一代程式員養成了「程式 = 可推演的數學對象」的直覺
  • 這種直覺是物件導向(封裝=結構化的延伸)與測試驅動開發(正確性可設計)的心理地基
  • 而動態語言時代(14 章)與 AI 時代(17 章)的「快速原型」文化,實際上是對 Dijkstra 嚴格主義的反彈——紀律與速度的鐘擺,每二十年擺一輪

小結#

概念 內容 遺產
GOTO 有害論 文本結構應與執行結構同構 可讀性成為工程核心價值
Böhm–Jacopini 順序/選擇/迴圈足夠 結構化有理論保證
軟體危機(NATO 1968) 複雜性失控被制度化命名 軟體工程學科誕生
Dijkstra 的嚴格主義 正確性靠設計不靠除錯 形式化方法傳統

ALGOL 與結構化運動為「組織大型軟體」備好了地基。第三篇的兩位主角——C 與 Smalltalk——將從「系統」與「建模」兩端接手。


下一節:9.1 指標與記憶體