

# 《计算机程序的结构和解释》前言，1984 年

教育家、将军、营养师、心理学家和家长计划。军队，学生，还有一些社团都是程序化的。解决大问题需要一系列的程序，其中大部分都是中途产生的。这些节目充斥着似乎是手头问题特有的问题。要欣赏编程本身就是一种智力活动，你必须求助于计算机编程；你必须阅读和编写计算机程序——其中许多程序。这些程序是关于什么的，或者它们服务于什么样的应用程序，都没有多大关系。重要的是它们的表现如何，以及在创建更好的程序时，它们与其他程序的配合有多顺畅。程序员必须寻求部分的完美和集合的充分。在本书中,“程序”的使用集中在用 Lisp 方言编写的程序的创建、执行和研究上，以便在数字计算机上执行。使用 Lisp，我们并没有限制我们可以编程的内容，只是限制了我们程序描述的符号。

我们对本书主题的理解涉及到三个现象焦点:人类思维、计算机程序集合和计算机。每一个计算机程序都是一个在头脑中孵化的模型，一个真实的或心理的过程。这些源于人类经验和思想的过程数量巨大，细节错综复杂，而且在任何时候都只能被部分理解。我们的计算机程序很少能模拟出令我们永远满意的模型。因此，即使我们的程序是精心手工制作的符号的离散集合，连锁功能的马赛克，它们也在不断进化:随着我们对模型的感知加深、扩大、概括，我们改变它们，直到模型最终在我们与之斗争的另一个模型中达到亚稳态。与计算机编程相关的兴奋的来源是在头脑中和在计算机上以程序表达的机制的持续展开和它们产生的感知的爆发。如果艺术解释了我们的梦，计算机就以程序的形式执行它们！

尽管计算机很强大，但它是一个苛刻的监工。它的程序必须是正确的，我们想说的话必须在每个细节上都准确无误。就像在每一个其他的象征性活动中一样，我们通过争论确信程序的真实性。Lisp 本身可以被赋予一个语义(顺便说一下，另一个模型)，如果一个程序的功能可以被指定，比方说，在谓词演算中，逻辑的证明方法可以被用来进行一个可接受的正确性论证。不幸的是，随着程序变得越来越大和复杂，就像他们经常做的那样，规范本身的充分性、一致性和正确性变得值得怀疑，因此完整的正确性的形式论证很少伴随着大型程序。因为大程序是从小程序发展而来的，所以我们开发一个标准程序结构的武器库是至关重要的，我们已经确定了这些标准程序结构的正确性——我们称之为习惯用法——并学会使用有价值的组织技术将它们组合成更大的结构。这些技术将在本书中详细讨论，理解它们对于参与名为编程的创新事业是必不可少的。最重要的是，强大的组织技术的发现和掌握加速了我们创建大型重要项目的能力。相反，由于编写大型程序非常费力，我们被刺激去发明新的方法来减少大量的功能和细节以适应大型程序。

与程序不同，计算机必须遵守物理定律。如果它们希望快速运行——每个状态变化几纳秒——它们必须只将电子传输很短的距离(最多 1.5 英尺)。空间中如此集中的大量设备所产生的热量必须被移除。一门精致的工程艺术在功能多样性和设备密度之间取得了平衡。无论如何，硬件总是在比我们关心的编程水平更原始的水平上运行。将我们的 Lisp 程序转换成“机器”程序的过程本身就是我们编程的抽象模型。他们的研究和创造给了与编程任意模型相关的组织程序大量的洞察力。当然，计算机本身也可以这样建模。想想看:最小的物理开关元件的行为是通过微分方程描述的量子力学来模拟的，微分方程的详细行为是通过在计算机上执行的计算机程序中表示的数值近似来捕捉的，计算机程序由以下组成。。。！

分开确定这三个焦点不仅仅是战术上的方便。尽管，正如他们所说的，这都是头脑中的，这种逻辑上的分离导致了这些焦点之间符号交通的加速，这些焦点的丰富性、活力和潜力在人类经验中只有生命本身的进化才能超越。在最好的情况下，焦点之间的关系是亚稳态的。计算机总是不够大或不够快。硬件技术的每一次突破都导致更大规模的编程企业、新的组织原则和抽象模型的丰富。每个读者都应该定期问自己“向什么目标前进，向什么目标前进？”—但是不要问得太频繁，以免你因为苦乐参半的哲学的便秘而错过编程的乐趣。

在我们编写的程序中，有些(但永远不够)执行精确的数学功能，例如排序或寻找一个数字序列的最大值，确定素性，或寻找平方根。我们称这样的程序为算法，关于它们的最佳行为，特别是关于执行时间和数据存储需求这两个重要参数，我们已经知道了很多。程序员应该掌握好的算法和习惯用法。即使一些程序抵制精确的规范，程序员也有责任评估并总是试图改进它们的性能。

Lisp 是一个幸存者，已经使用了大约四分之一世纪。在活跃的编程语言中，只有 Fortran 有更长的寿命。这两种语言都支持重要应用领域的编程需求，Fortran 用于科学和工程计算，Lisp 用于人工智能。这两个领域仍然很重要，他们的程序员如此专注于这两种语言，以至于 Lisp 和 Fortran 很可能会继续活跃地使用至少四分之一世纪。

口齿不清的变化。本文中使用的 Scheme 方言是从最初的 Lisp 发展而来的，在几个重要方面与后者不同，包括变量绑定的静态范围和允许函数以值的形式产生函数。在语义结构上，Scheme 与 Algol 60 和早期的 Lisps 非常相似。Algol 60 永远不再是一种活跃的语言，它存在于 Scheme 和 Pascal 的基因中。除了这两种语言之外，很难找到另外两种语言是两种不同文化的沟通渠道。帕斯卡赞成建造金字塔——雄伟壮观、令人屏息的静态建筑，由军队将沉重的石块推到合适的位置。Lisp 是用来建造有机体的——壮观的、令人屏息的、动态的结构是由一组组将无数波动的简单有机体安装到位而建造的。这两种情况下使用的组织原则是相同的，除了一个非常重要的区别:委托给单个 Lisp 程序员的可自由导出的功能比 Pascal enterprises 中的功能多一个数量级。Lisp 程序用函数扩充了库，这些函数的效用超越了产生它们的应用程序。Lisp 的本地数据结构 list 在很大程度上是这种效用增长的原因。列表的简单结构和自然适用性反映在非常不同步的函数中。在 Pascal 中，过多的可声明数据结构导致了函数内的专门化，这抑制并惩罚了偶然的合作。在一个数据结构上运行 100 个函数比在 10 个数据结构上运行 10 个函数要好。因此，金字塔必须保持一千年不变；有机体必须进化或灭亡。

为了说明这种差异，请将本书中对材料和练习的处理与任何使用 Pascal 的第一课教材中的处理进行比较。不要误以为这是一篇只有麻省理工学院才能理解的文章，是那里的学生所特有的。这正是一本关于 Lisp 编程的严肃书籍所必须具备的，不管学生是谁，也不管在哪里使用它。

请注意，这是一本关于编程的教科书，不像大多数 Lisp 书籍那样是为从事人工智能工作做准备。毕竟，随着被调查的系统变得越来越大，软件工程和人工智能的关键编程问题往往会结合在一起。这解释了为什么在人工智能之外，人们对 Lisp 越来越感兴趣。

正如人们对其目标的期望，人工智能研究产生了许多重要的编程问题。在其他编程文化中，这一系列问题催生了新的语言。事实上，在任何非常大的编程任务中，一个有用的组织原则是通过语言的发明来控制和隔离任务模块内的流量。当一个人接近我们人类互动最频繁的系统的边界时，这些语言往往会变得不那么原始。结果，这样的系统包含了重复多次的复杂的语言处理功能。Lisp 具有如此简单的语法和语义，以至于解析可以被视为一项基本任务。因此，解析技术在 Lisp 程序中几乎不起作用，语言处理器的构造也很少阻碍大型 Lisp 系统的发展和变化。最后，正是这种语法和语义的简单性给所有 Lisp 程序员带来了负担和自由。任何规模的 Lisp 程序，超过几行，都不可能不被自由选择的函数所饱和。发明和适合；有适合和改造！我们为那些把自己的想法写在括号里的 Lisp 程序员干杯。

—康涅狄格州纽黑文市艾伦·J·佩利