# 布莱恩·克尼汉：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://lexfridman-cn.pages.dev/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 就当它没有。**知道自己在依赖什么，尤其是在安全敏感的场合。几十兆你不认识的代码进到系统里，这件事值得停一下想想。
- **把数据当成一面镜子。**如果模型学到了奇怪的东西，先回去看数据里有什么，而不是先调模型。偏见在数据里，它就会在学习结果里。
- **别给糟糕的代码写注释，重写它。**这一条适用面比编程宽得多。任何靠解释维持的东西，都值得先问一句，能不能换一个模型让它不需要解释。
