# 布萊恩·克尼漢：UNIX、C、AWK、AMPL 與 Go 程式設計

> 親歷者講述 UNIX 與 C 語言的誕生，以及 AWK、AMPL 和 Go 背後的設計思想與工程文化。

- 原標題：#109 – Brian Kernighan: UNIX, C, AWK, AMPL, and Go Programming
- 期數：第 109 期
- 釋出日期：2020-07-18
- 嘉賓 / 主題：布萊恩·克尼漢：UNIX 與程式語言
- 網頁版：https://lex.qiaomu.ai/tw/ep/brian-kernighan/
- 官方頁面：https://lexfridman.com/brian-kernighan/
- 精讀字數：約 12333 字（閱讀約 27 分鐘）

> 本文為 lex.qiaomu.ai 基於原始訪談的 AI 改寫中文精讀，非官方翻譯；引用請註明出處並連結網頁版。

1969 年，Ken Thompson 的妻子帶著一歲的兒子去加州探親，為期三週。他獨自留在新澤西，用這三週寫下了一個作業系統。

五十年後，世界上絕大多數計算機執行的都是它的後代。

**這個系統叫 Unix。**

布萊恩·克尼漢（Brian Kernighan）就在現場。他在貝爾實驗室待了很多年，和 Ken Thompson、Dennis Ritchie 是同事，與 Ritchie 合著了《C 程式設計語言》，還共同創造了文字處理語言 AWK 和代數建模語言 AMPL。Lex Fridman 在節目開頭說他是"我在這個播客裡聊過的最謙遜、最友善的人之一"。而克尼漢整場對話反覆說的一句話是：我不行，那主要是別人的功勞。

這種謙遜是真的。可他參與創造的東西，構成了今天幾乎所有軟體開發的地基。他自己在節目裡開玩笑說，AWK 現在還有數百萬人在用，所以"你的愚蠢錯誤也擺在他們面前讓人看"。這句話其實才是這篇文章的入口：一個站在技術史關鍵節點上的人，怎麼看待自己參與締造的世界。

## 三週，一個作業系統

要理解 Unix 為什麼會出現，得先回到沒有它的年代。

1950 年代到 1960 年代初，大多數計算在物理上巨大的機器上完成，體積大，按今天的標準卻一點都不快，被安放在專門的機房裡。你寫程式用打孔卡，把一疊卡片寫好，交給櫃檯，櫃檯轉給操作員，過一陣子回來的答覆往往是"你這裡寫錯了"，然後你回去改，重來。整個迴圈非常慢。

分時系統的想法是：還是那臺機器，但用類似電動打字機的終端接上去，讓每個人輪流用一小片時間。你敲一個鍵，作業系統醒來，記下這個字元，然後回去處理別人的事。它繞著所有人轉圈，給每個人一小塊時間，製造出一種"這臺機器基本是我一個人的"錯覺。

**這種錯覺是好東西。它比打孔卡模式好太多，而且它催生了整個互動式計算的傳統。**

第一個比較像樣的分時系統叫 CTSS，相容分時系統。1966 年夏天，克尼漢作為實習生用過它，主持者是 Fernando Corbató，一位剛在這年去世的圖靈獎得主。CTSS 跑在一臺 IBM 7094 上，記憶體被擴到常規的兩倍：兩組 32K 字，每字 36 位，換算下來一次大概 150KB 乘二。今天這點記憶體在噪聲裡都找不到，當年卻是一筆鉅款。

CTSS 之後是 Multics。名字是 Multiplexed Information and Computing Service，多重資訊與計算服務。它的野心是做一個"資訊公用事業"，可以讓計算像牆上的插座一樣即插即用。你聽這個概念會覺得很熟，因為這基本就是今天雲端計算的描述，只是早了五十年。

Multics 由 MIT、貝爾實驗室和通用電氣三家一起做，通用電氣提供硬體。麻煩在於它太貴，承諾的東西交付得太晚。1969 年初，貝爾實驗室決定退出。

這一退，留下了一批已經嘗過好環境滋味、現在卻無環境可用的人。他們開始做紙面設計，想一個新的作業系統該長什麼樣。同時 Ken Thompson 找到一臺使用不多的 PDP-7，開始拿它做檔案系統的實驗，琢磨資訊該怎麼高效地存到計算機裡。然後他妻子去了加州，他坐下來，把它寫成了一套作業系統。

克尼漢對這段有個明確的糾正。很多人以為他也是 Unix 的作者之一，他本人說："我和它沒有任何關係，我從沒寫過作業系統程式碼。"他說得很清楚，真正的作者是 Ken Thompson，加上 Dennis Ritchie 和周圍一批人的巨大貢獻。

> "Ken Thompson 是我見過的程式設計師裡的一種奇異點。沒有對你不敬的意思，也沒有對我自己，但他現在已經在好幾個量級之外了。"（Brian Kernighan）

三週寫一個作業系統，聽起來像神話。這裡面確實有運氣成分，有那種"某個人在某個時間點恰好出現"的時機問題。但克尼漢也提醒，那時候的系統按今天的標準並不大。早期的 Unix 就是一段程式碼，讓你能執行程序，能存檔案，能載入可執行程式碼到記憶體，交給它控制權，跑完再收回來。再加上一個彙編器、一個文字編輯器、一套檔案系統，核心就齊了。這些在 1973 到 1974 年之前，全是用 PDP-7 組合語言寫的。

用匯編寫複雜系統難到什麼程度？克尼漢說，在這種語言裡你犯了錯沒人會告訴你，沒有輔助輪。你只能靠某種看全域性的能力，把東西一點點拼起來。他自己已經很久沒寫過彙編了，但那種"沒有訓練輪"的感覺他記得很清楚。

## 貝爾實驗室：傳奇是怎麼被養出來的

貝爾實驗室當時在新澤西有好幾處，主址在 Murray Hill，跨在兩個小鎮的邊界上，從紐約市往西大約十五到二十英里。那裡有三四千人，很多是博士，主要做物理科學，化學、物理、材料，數學也很強，同時對計算的興趣越來越大，因為人們發現可以用計算機去建模、去替代一部分實驗室工作。

它隸屬於 AT&T。AT&T 提供的是全美的電話服務，貝爾實驗室的目標就是讓這個服務不斷變好。這個目標聽起來平淡，但它決定了一切：為了改進電話服務，你需要做電晶體，需要做光纖，需要做微波系統，需要研究材料、器件和數學。指標看起來是服務，實際落到研究上，就是有大量基礎問題可以啃。

克尼漢強調那個環境非常開放、非常合作，友好到有點不像大公司。你有問題就去找人，對方可能就幫你解決。人們會幹一些奇怪的事，時不時地戳一戳官僚體系，帶點反叛的意味。

他特別提到物理上的接近：貝爾實驗室本質上是一棟巨大的樓，做相關工作的人擠在兩三個樓區裡，還有一個五十英尺乘五十英尺左右的公共空間，放著咖啡機，人們在那裡閒逛。反饋迴圈非常短。你早上有個想法，晚上或者第二天就有一個能跑的東西，別人會來試，會說"這很棒，但你這裡搞砸了"。

這是不是獨一無二？克尼漢立刻用了"倖存者偏差"這個詞來打自己。他說，如果這事沒發生在貝爾實驗室，別的地方也在做同樣有意思的工作。施樂帕克研究中心（Xerox PARC）就是最明顯的例子，貢獻了多得驚人的東西，很多我們今天習以為常的技術都出自那裡。只不過帕克的母公司可能沒那麼幸運，沒有把這些成果好好變現。

回到程式設計本身。克尼漢說，早年程式設計的樂趣之一是有大量的低垂果實，前人沒做過，環境又是新的，工具好用，系統響應快，同事極強。他把這種體驗總結成一件事：**程式設計本身是解謎，是好玩，但真正讓好玩變得更好的，是你做的東西有人用。哪怕他們在抱怨它壞了，他們用了，這件事本身就是獎勵。**

## Unix 的設計哲學：為程式設計師服務

Unix 的根本目標，按克尼漢的說法，是提供一個讓人容易寫程式、寫程式高產的環境。它的定位就是為程式設計師服務的環境，別的用途都是順帶。

這一點值得細讀。它被大量用於文書處理，但它不是作為文書處理系統設計的；被大量用於實驗室控制，但它不是為此設計的；被大量用作大型系統的前端，也不是為此設計的。它就是讓寫程式變得容易。相應地，寫出來的程式也常常是給別的程式設計師用的，於是形成了一個正迴圈：環境越好寫程式，就有人寫越多有用的程式，越多有用的程式又讓環境更有價值。

Dennis Ritchie 在書末有個觀察，說從他的角度看，真正的目標其實是創造一個社群，一群人可以在這個系統上作為程式設計師一起工作。這個說法比任何技術描述都更貼近 Unix 的本質。

Unix 的效率和健壯性從哪裡來？克尼漢給的理由很樸素：它從極其簡陋的硬體上開始，處理器速度和記憶體都非常有限，這逼著你不能亂來。沒有資源去堆複雜的東西，於是你被迫去找一個機制通吃許多場景，而不是寫一堆特殊分支。

**檔案系統是最好的例子。Unix 的檔案介面極其直接，這讓圍繞它寫程式碼非常高效。然後有人發現，這個介面不只能管磁碟檔案，還能管裝置：對裝置讀寫和對檔案讀寫是一回事。**

這個泛化思路後來被推得更遠。Plan 9 作業系統在 1980 年代末出現，嘗試把更多資源都塞進同一個介面，程序、網路、各種檔案系統，全部共享那套統一的抽象。每種資源用同一把鑰匙去開，用的人就不用每次都重新想一遍。

這裡藏著一條可以帶走的規律：**找到正確的模型，能讓很多事同時變簡單。**省下來的認知成本，會轉化成人人都能多做一些有用的事。

## 一個"開源"的誤會

Lex 問到一個很常見的印象：Unix 後來成了開源系統，因為 AT&T 受各種約束，沒法自己做計算機生意。

克尼漢直接糾正了這個說法。Unix 從來不是開源的，它是明確的專有許可（proprietary）。只是它以原始碼形式，長期免費授權給大學。正是這一點，讓一代代學生和老師看著 Unix 長大，也讓社群裡積累起足夠的專業能力，人們才有可能各自分頭去搭一個"長得像 Unix"的東西。

伯克利版的 Unix 從這個許可程式碼起步，逐漸加入自己的貢獻，尤其來自 Bill Joy 等人的程式碼。加得足夠多之後，它最終能完全擺脫 AT&T 的程式碼。1980 年代末到 1990 年代初，圍繞這件事打了大量法律官司。

開源運動這個說法，大致可以從 Richard Stallman 在 1980 年代末的想法算起。到 1991 年 Linus Torvalds 決定寫一個類 Unix 作業系統時，社群裡已經有足夠多的知識存量。他能看見目標，因為 Unix 的系統呼叫介面、工具集早就在那裡擺著。於是他搭出的是一個到今天說起"Unix"，你腦子裡其實想的是 Linux 的東西。

所以那句"技術上不開放，技術上屬於 AT&T，但在實踐中相當開放"，是對這件事最準確的概括。

## AWK 與 grep：一行程式碼的威力

AWK 是克尼漢和 Al Aho、Peter Weinberger 在 1970 年代末做的，名字來自三個人姓氏的首字母。它的定位是讓"又快又髒"的任務變得容易：數個數、從文字里挑出有意思的資訊、換個方式重排、總結一下、對每一行跑一條命令。

它到今天仍然被廣泛使用，已經四十多年了。克尼漢說，他們當初沒意識到，這個模型其實對很多資料處理任務都很好用，所以它能活這麼久。他半開玩笑地給了個建議：**如果按"投入產出比"來算，學 AWK 大概是學一門程式語言裡價效比最高的。**它撐不起大專案，但在那些"我就想看看這裡面所有的某某東西"的小事上，表現相當好。

AWK 之所以順手，很大程度上在於它的預設行為設計得好。它自動逐檔案、逐行讀，每一行上你用一組模式和動作來寫程式：如果某個模式匹配，就執行對應的動作。相當於一個四層巢狀迴圈，全部自動完成，你只要寫模式和動作。它還自動把行切成欄位，自動收集資訊，比如這是第幾行、這行有幾個欄位。

結果是，在別的語言裡要五到二十行的東西，在 AWK 裡一兩行就夠。因為只有一兩行，你可以直接在命令列裡做，不用另開一個環境。

grep 更簡單。它搜輸入裡符合某個正規表示式的行，模式匹配就列印。它和 AWK 是同一種正規化，只是模式只有一個、動作也固定成"列印"。克尼漢說自己現在可能用 grep 比用任何別的東西都多，因為太方便、太自然。

為什麼 Windows 一直沒有原生的 grep？克尼漢的猜測是歷史包袱加設計取向。MS-DOS 是個相當糟糕的作業系統，1990 年代某個時候 Windows 變成圖形系統，微軟把能量都投在這個圖形介面上。那是一種面向非程式設計師的計算模型：點、點、點，試選單。對只想把事做完的人，這個模型其實挺好用，反而是把命令列教給非程式設計師常常是一場艱難的 uphill battle。

"從微軟的角度，他們做的大概是對的。"克尼漢說。長期以來的替代方案是 Cygwin 這類東西，把 Unix 和 Linux 上你喜歡的工具全部搬過去，讓它們在 Windows 上正常工作，這套東西已經存在至少二十年。今天還有 WSL，能讓你在 Windows 裡寫 Bash 指令碼。Lex 說自己用過之後"生活都變了"。

## 編輯器考古：從紙張到 vi

克尼漢平時用什麼編輯器？Sam，Rob Pike 很久以前在貝爾實驗室寫的。它的年代晚於 vi 和 Emacs，源頭可以追溯到最初的 Unix 編輯器 ed。

這段歷史挺有意思，因為它解釋了今天的編輯器為什麼長成這樣。最早的分時系統上有編輯器，完全基於命令。那時候沒有滑鼠，甚至沒有游標，因為終端是把字打在紙上的。Ken Thompson 寫過 QED，功能非常強。Unix 出現後，他把 QED 大幅削減，變成了 ed：一個行編輯器。你可以載入檔案，然後按行操作，列印某幾行，加文字、刪文字、改文字，或者用替換命令改行內的內容。它的介面完全基於命令列，輸出打在真紙上。**你改一行，就得把那一行重新列印出來，用掉另一行紙，才能看到改成了什麼。**

CRT 顯示器出現以後，才有可能用游標控制，才能在螢幕上移動位置而不用重新列印一切。那個年代出現了好幾個編輯器，克尼漢最熟悉、至今偶爾還在用的是 Bill Joy 寫的 vi，大概出自 1970 年代末，充分利用了游標控制。Emacs 大約同時，但克尼漢說自己從來沒把 Emacs 內化過。

所以他現在的主力是 Sam，有時用 vi。Sam 在大多數系統上都能弄到，通常要從 Plan 9 發行版裡自己下載。

## 從彙編到 JavaScript：語言的上升史

克尼漢給的程式語言簡史，從打孔卡和麵板開關開始。最早人們用零和一對機器程式設計，後來有了彙編器，把 add 這樣的助記符翻譯成對應的位模式，順便處理"這個東西在記憶體哪裡"的瑣碎工作。

他引用了一個剛過世的例子：Tony Brooker 在曼徹斯特做的 Auto code。它的前身是 Alan Turing 寫的一套語言，你得把指令用零和一寫進去，而且要**倒著**寫，因為硬體就那麼工作的。

彙編時代的特點是每臺機器一套組合語言，IBM 有它的，曼徹斯特有它的，各不相同。彙編指令和機器的指令集基本一一對應。你必須非常瞭解這臺機器才能寫程式，而且換一臺機器就得從頭開始。

1950 年代末，人們意識到可以把這套遊戲再升一級，造出更接近人類思考方式的語言。那幾年出現了 FORTRAN，來自 IBM，是 Formula Translation，讓科學和工程計算變容易；出現了 COBOL，Common Business Oriented Language，Grace Hopper 等人參與，面向商業任務；出現了 ALGOL，主要用來描述演算法；Basic 稍微晚一點。

這次升維帶來兩件事。**第一，程式只寫一次。**不同的編譯器可以為不同硬體編譯，程式不再綁死在一種機器上。**第二，寫程式碼這件事被民主化了。**一個物理學家或工程師，自己就能把程式寫出來，不必去請專業程式設計師。寫出來的質量可能不如專業程式設計師的，但已經夠用。

1970 年代出現系統程式語言，C 是唯一的倖存者。這類語言要能幹系統程式的事，比如文字編輯器、彙編器、編譯器，甚至作業系統本身，所以它得能管記憶體、能訪問程序，和機器打交道的程度更高，但方式更可控。之後是物件導向，C 寫到一定規模就撐不住了，太多碎片沒法追蹤，C++ 從那條傳統裡長出來。再然後就是語言的爆炸，Java、JavaScript，一路到今天。

## C 的甜蜜點

為什麼 C 能活下來，影響這麼深？克尼漢的說法是它卡在了一個甜蜜點上：**表達能力足夠讓你自然地重寫東西，同時效率也夠高。**在高效率尤其重要的年代，這兩點很關鍵。把時間倒回五十年前，那就是四五代摩爾定律之前，機器的能力差得遠。

還有一個因素是環境。C 和 Unix 綁在一起，寫出來的程式可以在所有跑 Unix 的機器上用，而所有這些機器又都是用 C 寫的。**作業系統本身可移植，工具也可移植，它們互相餵養，形成正迴圈。**

《C 程式設計語言》這本書 1977 年開始寫。當時的時機很好：Unix 正在擴散，從幾十個系統漲到幾百個，C 也在和 Unix 無關的機器上出現。市場上沒有別的 C 書，貝爾實驗室是唯一權威來源。Dennis Ritchie 本人就是權威，因為他設計了這門語言，還寫過一份極其出色的參考手冊。克尼漢說他"擰著 Dennis 的胳膊"，直到他同意一起寫書。

書裡最有名的遺產大概是 Hello World。克尼漢對"好例子"的看法很具體：一個好的例子會告訴你如何做一件事，而且它代表的東西，你未必想原樣照做，但你多半想做同一類的事。他花了很多時間找那種能連成一串、能告訴讀者"你需要知道什麼、以及什麼時候會需要"的例子。

他對很多程式設計書的抱怨是：不給現實的、你可能會真的想做的例子，只給純語法。"這裡怎麼把三個數加起來，得了吧，這個我自己能想出來。告訴我這三個數怎麼進到計算機裡，做完有用的事以後，怎麼又整整齊齊地輸出回來。"

這句話背後是一種很樸素的教學觀：**人是從能用的東西里學的，不是從語法清單裡學的。**

## Go：為 21 世紀準備的 C

Go 有兩個源頭。一半是貝爾實驗室的傳統，三位創造者裡的 Ken Thompson 和 Rob Pike 都是那裡的人。另一半來自歐洲學派，經由 Robert Griesemer 傳過來，他算是第二代下傳的學生。這個組合挺有意思。

在克尼漢看來，Go 抓住了 C 裡好的那部分：表面上看非常像 C，有人乾脆把它叫做"21 世紀的 C"。同時它又引入了不錯的資料結構能力。他自己不是 Go 專家，雖然合著了那本 Go 的書，但他說那本書 90% 的工作是另一位作者、真正的 Go 專家 Alan Donovan 做的。

他認為 Go 特別有用的地方是併發模型。它基於 Tony Hoare 四十多年前提出的"通訊順序程序"（CSP）。goroutine 在他看來是描述平行計算非常自然的方式，寫起來容易，通常能正常工作，效率也不錯。

順帶問一句，早期 Unix 的人有沒有預見到執行緒和平行計算？克尼漢猜沒有。也許有人看到了，但很長時間裡，這不是一件你必須去處理的事。處理器越來越快，然後因為功耗和散熱這類問題，不再變快，於是轉向往一顆晶片上放更多處理器。並行的東西就是在這種背景下變得重要的。

## 用 20 種語言寫同一個程式

克尼漢有個習慣：課堂上他會拿一個小例子，儘可能用不同的語言寫一遍。現在手上有二十個版本。

這個例子本身很無聊：讀入長度不一的文字行，把每行重新折成不超過 60 個字元。在一門語言裡大概十五行，別的地方也差不多。他關心的三件事是：程式碼多長，跑多快，學起來多痛苦。

這當然是極窄的樣本，但他有他的道理。**"一門語言最難的一步，大概就是第一步。"**他用這個小程式去邁那第一步，然後看自己經歷了什麼。

結果差異很大。在 Lua 裡，他下載了 Lua 環境，一個小時就做出來了，之前一輩子沒寫過 Lua。Scala 同樣簡單，他試了，卻花了幾個星期，而且跑得像烏龜。FORTRAN 90 很痛苦，但能跑。Rust 花了幾天，記憶體管理的模型對他有點陌生。他當時遇到的最大問題是找不到一致可用的文件，感覺手冊裡的一切都在快速變化，看起來能跑的示例，在他的語言版本上跑不起來。這件事發生在幾年前，他說現在應該穩定多了。

他的總結落到實處：**你得有一件想做的事，還得有足夠可支配的時間，兩件都滿足，才值得去學一門新語言。**光為了學而學，這事永遠不會發生。

那 JavaScript 呢？當年它被公認為最醜的語言之一，學術界提起它會嘲笑你，說它不配被學術研究。克尼漢認為相當一部分已經變了：語言本身在演進，編譯技術更是好得不可思議，所以前後端都已經是可行的方案。他自己寫過一些，也玩過一些翻譯器，但不是專家，這裡變化太多，他也跟不上。

## 庫的甜蜜負擔

Lex 問了一個很敏銳的問題：今天寫 Python 或 JavaScript，很大一部分體驗其實是在用別人寫好的庫，這和當年在 Unix 和 C 上從零開始搭東西，感覺完全不同。

克尼漢承認這是關鍵差異。舊時代程式設計之所以好玩，原因之一是你真的在全部自己搭，要處理的庫相當少，最多加一個標準庫。今天不是這樣。

他的描述很具體：這幾天的機器學習的嘗試裡，有東西不工作，你就 pip install 某個包，幾百兆的不知道什麼東西就下來了，你完全不知道它是什麼。運氣好它就能用，運氣不好你毫無辦法，而且你根本沒法從這一千個包裡找出是哪個出的問題。

他覺得 JavaScript 的 NPM 環境更糟，紀律和管控都更少。這類問題的後果不只是"你不懂它怎麼工作"，還包括安全問題、健壯性問題。"所以，你不想用 JavaScript 去跑一個核電站，大概。"他說得像是玩笑，但半是玩笑的東西往往最真實。

## AMPL：把數學翻譯給求解器

AMPL 是克尼漢參與設計的代數建模語言，和 Bob Fourer、Dave Gay 一起做的。這件事在他自己的說法裡，主要功勞也歸兩位搭檔：Bob 是西北大學工業工程與管理科學系的教授，Dave 是貝爾實驗室的數值分析和最佳化專家。

線性規劃是什麼？你有一堆決策變數，它們必須以某種方式組合，滿足一組約束，然後你要找到一個取值，讓某個目標函式最大或最小。教科書裡通常寫成：一個大矩陣 A，乘以向量 x，小於等於向量 b，再加一個目標函式，是若干 x 的線性組合。

問題在於真實世界裡的 A 非常龐大、非常稀疏、結構非常複雜，各個元件分散在係數裡，人幾乎沒法直接看懂。你在黑板上寫的是數學："這一堆的和大於那一堆的和"，而你真正需要的是把黑板上的數學直接寫成約束，再交給求解器。

Bob 長期以來對建模語言有興趣。當時已經有一個叫 GAMS 的通用代數建模系統，但語法像 FORTRAN，很笨重。1984 年，Bob 到貝爾實驗室做休假年，辦公室就在克尼漢對面，地理上非常近，於是兩個人加上 Dave 一起開始談這件事。

那種語言應該寫成什麼樣子？無非是像黑板上的代數式，用文字寫出來。克尼漢寫了 AMPL 的第一版，這也是他人生第一個 C++ 程式，大概三千行，不算大，但它證明了這件事可行：人可以自然地寫模型，機器能把它轉換成求解器能處理的東西。

關鍵設計是模型和資料分開。同一個模型可以用在完全不同的資料上，就像很多程式那樣。求解器負責所有硬活，AMPL 把求解結果轉換回你原來的形式，告訴你每樣東西該買多少、做多少、運多少。

Lex 提到他自己用 AMPL 時，會把模型發給確定沒程式設計背景的同事看，對方也能大致理解那個最佳化問題是什麼。**一個語言能讓人讀懂問題本身，這件事比它跑得多快重要。**

## P 等於 NP 嗎？

Lex 逼克尼漢下注：你覺得 P 等於 NP 嗎？

答案是"不"。他說自己沒有直覺，但讀到的很多同行都這麼下注，這是流行看法。他還轉述了一個關於 Jeff Dean 的段子，問他在什麼條件下 P 會等於 NP，回答大概是"要麼 P=0 要麼 P=1 之類"，克尼漢說自己記不清了，反正那人比他聰明太多。

克尼漢博士論文寫的是關於大 O 符號的東西，1968 年，早於計算複雜性理論的成形。他當時做的是圖劃分：給定一個有節點有邊的圖，邊有權重，你要把節點分成兩堆、大小相等，讓跨越兩堆的邊數儘可能少。這個問題很難。

他和 Shen Lin 在貝爾實驗室一起做，始終沒能找到任何保證給出正確答案的演算法。他們做出了效果相當好的啟發式方法，他也為論文挑出了一些特殊情形，但就是難。**那個時間點，Steve Cook 正在證明有一類問題看起來真的很難，圖劃分是其中之一。**

這裡有個細節值得琢磨。Lex 拿最大割作對比：最大割和圖劃分是同一個問題，只是不限制兩邊節點的數量，而一旦加上這個約束，它就變難了。約束的存在與否，把一個容易問題變成了難問題。

## AI 的夏天、冬天和新夏天

1964 年，克尼漢在多倫多讀本科，畢業論文是一篇關於人工智慧的文獻綜述。那正好是 AI 的夏天，人們以為計算機什麼都能幹：機器翻譯、下棋、證明幾何定理，樣樣都能。他讀了《計算機與思維》那本論文集，某種程度也算喝了一點 Kool-Aid，人非常樂觀。

後來大家都知道了，當時以為幾年就能實現的東西，花了幾十年。下棋和下圍棋現在確實比人強了，機器翻譯也好了很多，但這背後是五六十年的進步、硬體的演進，以及大量可以用來學習的資料，再加上網際網路和開源讓開發者協作的基礎設施。

從這裡能抽出什麼教訓？克尼漢引用了科幻作家 Arthur Clarke 的一句話：**一位德高望重的老人說某件事可能，他多半是對的；說某件事不可能，他幾乎肯定是錯的。但沒人知道時間尺度。**

談到今天這波機器學習和神經網路，他說自己離專家非常遠，大部分是從學生那裡學的，學生們大概會對他學得這麼少感到失望。但他認為潛力很大，遊戲領域已經看到效果。同時他有一個實實在在的擔心：機器學習從我們給它的資料裡學，資料有問題，學到的東西就有問題。最明顯的例子就是偏見，如果資料因為歷史的對待方式而包含"某些人不如另一些人"這種明擺著錯誤的東西，機器學習就會把它傳播出去。

而這個擔心還有光明的一面。**機器學習做的事，是把資料裡的偏見暴露出來，像一面鏡子照向我們自己的社會，從而幫我們去修正它。**克尼漢說，如果它真的是這樣運作的，那會很棒。但他不知道它到底是不是這樣運作，或者反過來說，機器學習的機制會不會反而強化和放大過去就錯的東西。這件事必須嚴肅對待，不能含糊過去。

至於"造出人類水平的智慧需要什麼"，他的回答非常乾脆：我完全不知道。

## 摩爾定律撞上原子

指數增長能永遠持續嗎？克尼漢給了個俏皮的回答：沒有哪個指數能永遠持續，總會在某處耗盡。但如果它持續得足夠久，那可能就是我們需要的全部了，反正跟我們沒關係了。

他注意到摩爾定律已經在變。處理器不再變快，於是人們把"同樣面積塞更多東西"的能力用在橫向增長上：一顆晶片上放更多處理器、更多記憶體。這會撞到限制嗎？大概會，因為總有一天你會逼近單個原子，得另想辦法繞過去。會不會找到繞過去的辦法？不知道。他說這話時還補了一句：**"我剛說過，如果我說不會，我肯定會錯，所以也許我們會找到。"**

那程式語言會怎麼變？他的判斷是，會延續一些領域已經出現的趨勢：**更多程式設計由程式完成，更少由人完成；更多是宣告式，過程式的成分越來越少。**你只需要說我想要這件事發生，由別的東西去搞清楚怎麼做。今天這在許多垂直領域已經是常態，領域特定語言的天下。可以想象這個範圍會變寬，你不需要寫那麼多細節，某個軟體集合會弄清怎麼做你想做的事。

一句話概括：抽象層次會繼續升高。高到某一天，也許就到了能用自然語言描述意圖的地方。

## 計算機如何改變了人與人

克尼漢在普林斯頓教一門叫"計算機與我們世界"的課，面向非技術學生，典型的是歷史系或英語系的人。他要講計算機如何工作、程式設計是什麼、計算機之間如何通訊、通訊的時候在幹什麼。

這門課裡有個設計很有意思：他會帶學生看一臺玩具機器，指令集很小，讓學生寫幾行彙編，理解最底層是什麼樣，計算機是如何用一小撮非常簡單的指令（加減、跳轉之類）把事做完的。然後往上走到 FORTRAN、C 這樣的高層語言，看抽象層次如何讓人更容易寫程式碼、更不依賴具體架構。最後再講他們天天在用的那些程式：手機上的作業系統、下載的 App，全都是我們剛說過的這類東西，只是規模巨大。

他承認這是要求學生從五行彙編跳到一個手機瀏覽器，概念跨度巨大。但本質上，它們是一回事。

那麼計算機到底怎麼改變了世界？克尼漢抓了一個角度：人與人之間的互動方式。

他小時候，打一個電話到某個地方是件大事，因為要花不少錢，那是 1960 年代。今天人們不打電話了，發簡訊。人們覺得和世界任何角落的朋友、家人定期通訊、開視訊會議，是理所當然的。

這對人是好是壞？他給了個兩面兼有的答案。他自己和加拿大的兄弟姐妹發電郵，比二十年前打電話頻繁得多，大概每兩三天就會收到或發出一封，這在某種意義上把他們拉得更近了，是好事。但他看著校園裡的孩子，大多低著頭盯著手機走，他得躲著走。社會學研究裡有人指出，人們實際上反而不如以前親近了。他不知道這是不是真的，但他能看見潛在的下行面。

更宏觀的一層，他看到的是雙面性。計算機給了人們改善生活的可能，這是好的。同時短期內也能看到大量壞的一面：人變得更部落化、更狹隘，而且數量龐大。人們用計算機去誤導、歪曲，或者乾脆對正在發生的事撒謊，這件事正在影響各地的政治。

Lex 補了一句：現在每個人都有了發聲的渠道，被壓迫的人有了聲音，這是強大的事。**但每個人都有了聲音，隨之而來的混亂，看著既迷人又可怕。**

## 大多數人不該學程式設計，但該懂程式設計

回到那個最普通的問題：所有人都該學程式設計嗎？

克尼漢的回答很剋制，也很明確：大概不該。但每個人都應該至少理解程式設計是什麼。這樣當有人對你說"我是程式設計師"，你腦子裡有個大致的概念；當有人說"這是由計算機執行的程式決定的"，你對那意味著什麼有一個模糊但準確的直覺。

他的這門課就是幹這個的，講計算機怎麼工作，講程式怎麼寫，講計算機之間怎麼對話。他坦白說，這門課上極少有人後來成為嚴肅的程式設計師。但至少他們對這一整套東西有了更好的認識，而且不只是程式設計，還包括背後的技術和通訊。

課程裡學生要寫一點程式，很少。他帶他們看清機器在高層語言之下是怎麼運作的。雖然嚴格說沒有"最低層"，你總能再往下走，但他說，他在那裡畫了一條線。

## 那些真正開心的時刻

Lex 最後問，如果能回到過去重活一個瞬間，那個讓你真正快樂、或者深刻改變你的瞬間是哪一個？

克尼漢沒有給出某個具體時刻。他給出的是一類時刻，和地點繫結：在貝爾實驗室，你做出一件東西，它工作了，有人用了，說"這挺酷"。這種事在 1970 年代那個黃金時代發生得挺頻繁，Unix 還年輕，低垂果實遍地都是，一群人在一起，你做點東西，他們就會來試。

AWK 就是這樣一個例子。**他做出的東西，全世界幾百萬人天天在用。他也因此把自己所有的愚蠢錯誤都擺在這些人眼前讓人看。**他說這事既好又可怕。

Lex 把這一點說得很漂亮：它讓人脆弱，也美，因為它實實在在地影響了很多人。

克尼漢自己留在節目最後的那句話，只有七個字：**"不要註釋糟糕的程式碼。重寫它。"**

這句話可以當成他整場對話的一個總結。他不相信複雜的補救，不相信在壞東西上疊加解釋，不相信繞彎子。他相信的是把機制找對、把模型找對，然後讓一切自然地變簡單。這既是他參與締造 Unix 的方式，也是他看待程式設計、語言、工具、乃至技術與社會關係的方式。

如果你讀到這裡想做點什麼，下面這些是從這場對話裡能直接拿走的：

- **花一個下午學會 AWK 和 grep。**克尼漢說，按投入產出比算，它們大概是最值得學的工具。你不需要精通，會寫一行"從一堆文字里挑出某個東西並數一數"，就足夠在日常裡反覆省時間。
- **學一門新語言時，別從語法開始，從一件你想做的小事開始。**讀入若干行、重新折行、輸出，這種十五行左右的程式，一小時能跑通的語言，往往在預設行為設計上做得好。
- **寫程式碼之前先問清楚"這個問題真正要想清楚的是什麼"。**克尼漢把這一步歸為藝術，把"怎麼做才做好"歸為科學。多數人跳過前者，直接進入後者，然後在錯的路上狂奔。
- **接受"找到一個好模型"的價值。**Unix 的檔案介面之所以強，是因為一個機制通吃了很多場景。你手上的系統如果能找到一把這樣的鑰匙，後面會省下大量重複解釋和重複實現。
- **對庫保持敬畏，別一 install 就當它沒有。**知道自己在依賴什麼，尤其是在安全敏感的場合。幾十兆你不認識的程式碼進到系統裡，這件事值得停一下想想。
- **把資料當成一面鏡子。**如果模型學到了奇怪的東西，先回去看資料裡有什麼，而不是先調模型。偏見在資料裡，它就會在學習結果裡。
- **別給糟糕的程式碼寫註釋，重寫它。**這一條適用面比程式設計寬得多。任何靠解釋維持的東西，都值得先問一句，能不能換一個模型讓它不需要解釋。
