原文:Foreword
译者:飞龙
我有幸在我还是学生的时候见到了了不起的 Alan Perlis,并和他交谈了几次。他和我共同深爱和尊重两种非常不同的编程语言:Lisp 和 APL。跟随他的脚步是一项艰巨的任务,尽管他开辟了一条优秀的道路。尽管如此,我想重新审视他在这本书的原始前言中所做的一条评论(请阅读他的前言,它紧随在这个前言之后,然后再完成这个前言)。是否真的比起有 10 个函数作用于 10 个数据结构,有 100 个函数作用于一个数据结构更好?
要仔细回答这个问题,我们首先需要问这个数据结构是否“通用”:它是否可以方便地满足那 10 个更专门的数据结构的作用?
就此而言,我们还可以问:我们真的需要 100 个函数吗?是否有一个单一的通用函数可以满足所有其他函数的作用?
对于最后一个问题的令人惊讶的答案是“是的”;构造一个接受(1)作为某个其他函数描述的数据结构和(2)参数列表的函数,当应用于给定的参数时,其行为与该其他函数完全相同,只是稍微有些棘手。设计一个能够描述任何计算的数据结构也只是稍微有些棘手。这本书的第 4 章中描述了这样的一个数据结构(表达式和语句的标记列表表示,配对环境将名称与值关联)和这样一个通用函数(apply
)。所以也许我们只需要一个函数和一个数据结构。
理论上是这样的。实际上,我们发现方便的是划分出有助于我们作为构建计算描述的人类来组织代码结构,以便更好地理解它们的区别。我相信 Perlis 所说的不是关于计算能力,而是关于人类的能力和局限性。
人类大脑似乎擅长给事物命名;我们有强大的联想记忆。给定一个名称,我们可以快速地回忆起一些相关的事物。这就是为什么我们通常发现使用λ演算比组合演算更容易;对于大多数人来说,解释 Lisp 表达式(lambda (x) (lambda (y) (+ x y)))
或 JavaScript 表达式
x => y => x + y
比组合表达式更容易。
((S ((S (K S)) ((S ((S (K S)) ((S (K K)) (K +)))) ((S (K K)) I)))) (K I))
即使有直接的结构对应,可以用五行 Lisp 代码轻松表达。
因此,虽然原则上我们可以只使用一个通用函数,但我们更喜欢将代码模块化,给各个部分命名,并提到函数描述的名称,而不是不断地将描述本身提供给通用函数。
在我 1998 年的演讲“语言的成长”中,我评论说,一个优秀的程序员“不仅仅是编写程序。一个优秀的程序员建立了一个工作词汇。”当我们设计和定义程序的更多部分时,我们给这些部分命名,结果是我们有了一个更丰富的语言来编写其余的部分。
但我们也发现在数据结构之间划分出区别,并给它们命名是很自然的。
嵌套列表可能是一种通用的数据结构(值得注意的是,许多现代和广泛使用的数据结构,如 HTML、XML 和 JSON,也是括号嵌套表示,只是比 Lisp 的裸括号稍微复杂一点)。还有许多函数,比如找到列表的长度或将函数应用于列表的每个元素并得到结果列表,这些函数在各种情况下都很有用。然而,当我考虑特定的计算时,我经常对自己说:“我期望这个两个元素的列表是名字和姓氏,但我期望那个两个元素的列表是复数的实部和虚部,而我将把另一个两个元素的列表视为分数的分子和分母。”换句话说,我在区分——并且在数据结构中明确表示这些区分可能是有用的,部分原因是为了防止错误,比如意外地将复数视为分数。(再次强调,这是关于人类能力和人类局限性的评论。)
自本书第一版写成以来,几乎已经有将近四十年的时间,许多组织数据的方式已经相对标准化,特别是“面向对象”的方法,许多语言,包括 JavaScript,都支持对象、字符串、堆和映射等专门的数据结构,并具有各种内置机制和库。但在这样做的过程中,许多语言放弃了对更一般、通用概念的支持。例如,Java 最初也不支持一流函数,直到相对较近的时候才将其纳入,大大增强了其表达能力。
APL 最初也不支持一流函数,而且它最初的单一数据结构——任意维度的数组——并不像通用数据结构那样方便使用,因为数组不能包含其他数组作为元素。最近的 APL 版本确实支持匿名函数值和嵌套数组,这使得 APL 的表达能力大大增强。(APL 的原始设计确实有两个非常好的地方:一个是适用于该数据结构的全面函数集,另一个是非常合适的函数名称集。我说的不是有趣的符号和希腊字母,而是 APL 程序员在提到它们时使用的口头词汇,比如shape
、reshape
、compress
、expand
和laminate
;这些名称不是指代符号,而是指代它们所代表的函数。Ken Iverson 非常擅长为数组函数选择简短、易记、生动的名称。)
虽然 JavaScript 和 Java 一样最初设计时考虑了对象和方法,但它从一开始就纳入了一流函数,并且使用其对象定义通用数据结构并不困难。因此,JavaScript 并不像你想象的那样远离 Lisp,正如《计算机程序的构造和解释》的这一版本所展示的,它是一个呈现关键思想的良好替代框架。SICP从来不是关于一种编程语言;它提出了有用于任何语言的强大、通用的程序组织思想。
Lisp 和 JavaScript 有什么共同之处?抽象计算的能力(代码加上一些相关数据)以便以后作为函数进行执行;在数据结构中嵌入对这些函数的引用的能力;对参数调用函数的能力;区分的能力(条件执行);一个方便的通用数据结构;完全自动的数据存储管理(鉴于其他一切,这似乎是理所当然的,直到你意识到许多广泛使用的编程语言没有它);一大套对通用数据结构进行操作的有用函数;以及使用通用数据结构表示更专门的数据结构的标准策略。
所以也许真相在 Perlis 如此雄辩地提出的两个极端之间。也许最佳点更像是 40 个足够通用地在通用数据结构(如列表)上有用地操作的函数,但也有 10 组每组 6 个函数,当我们从 10 个专门的视角看待通用数据结构时是相关的。如果我们给这些函数和专门视角起好名字,这是可以管理的。
当你阅读这本书时,请不仅注意编程语言的构造和它们的使用方式,还要注意给函数、变量和数据结构起的名称。它们并不都像 Iverson 为他的 APL 函数选择的那样简短和生动,但它们是以一种有意识和系统的方式选择的,以增强你对整体程序结构的理解。
原语、组合方式、函数抽象、命名以及使用通用数据结构的约定方式来专门区分:这些是一个良好编程语言的基本构建块。从那里开始,想象力和基于经验的良好工程判断可以做剩下的事情。
—Guy L. Steele Jr.,马萨诸塞州列克星敦,2021
原文:Foreword to Structure and Interpretation of Computer Programs, 1984
译者:飞龙
教育家、将军、营养师、心理学家和父母都在进行程序设计。军队、学生和一些社会也在进行程序设计。对于大问题的攻击采用了一系列程序,其中大部分在途中出现。这些程序充满了似乎特定于手头问题的问题。要欣赏编程作为一种独立的智力活动,你必须转向计算机编程;你必须阅读和编写计算机程序——其中很多。程序的内容并不重要,它们服务于什么应用也不重要。重要的是它们的表现如何以及它们如何与其他程序顺利地结合以创建更大的程序。程序员必须追求部分的完美和整体的充分性。在这本书中,“程序”的使用集中在使用 Lisp 方言编写的、在数字计算机上执行的程序的创建、执行和研究上。使用 Lisp,我们限制或限定的不是我们可以编程的内容,而只是我们程序描述的符号。
我们与本书的主题有三个焦点:人类思维、计算机程序集合和计算机。每个计算机程序都是一个模型,在头脑中孵化出一个真实或心理过程的模型。这些过程源自人类的经验和思想,数量庞大,细节复杂,而且在任何时候只能部分理解。我们的计算机程序很少能完全满足我们对模型的要求。因此,即使我们的程序是精心制作的离散符号集合,是相互交织功能的马赛克,它们不断地演变:随着我们对模型的认识加深、扩大、泛化,我们改变它们,直到模型最终在我们努力的另一个模型中达到一个亚稳定的位置。与计算机编程相关的振奋源于头脑和计算机中不断展开的机制,这些机制被表达为程序,产生了感知的爆发。如果艺术诠释我们的梦想,那么计算机以程序的形式执行它们!
尽管计算机很强大,但它是一个严格的任务主人。它的程序必须是正确的,我们希望表达的内容必须在每一个细节上都准确无误。与其他符号活动一样,我们通过论证来确信程序的真实性。Lisp 本身可以被赋予语义(顺便说一句,这是另一个模型),如果一个程序的功能可以在谓词演算中指定,那么逻辑的证明方法可以用来进行可接受的正确性论证。不幸的是,随着程序变得庞大和复杂,几乎总是如此,规范的充分性、一致性和正确性本身变得值得怀疑,因此完整的正确性论证很少伴随大型程序。由于大型程序是由小型程序发展而来的,因此我们必须开发一套我们确信正确的标准程序结构的工具库——我们称之为成语,并学会使用经过验证的组织技术将它们组合成更大的结构。这些技术在本书中得到了详细的处理,理解它们对参与被称为编程的普罗米修斯企业至关重要。最重要的是,发现和掌握强大的组织技术加速了我们创造大型、重要程序的能力。相反,由于编写大型程序非常费力,我们被激发去发明新的方法来减少要适应大型程序的功能和细节的负担。
与程序不同,计算机必须遵守物理定律。如果它们希望快速执行——每个状态变化几纳秒——它们必须只传输电子的短距离(最多英尺)。由于这么多设备在空间中集中产生的热量必须被移除。一种精致的工程艺术已经发展出来,在功能的多样性和设备的密度之间取得平衡。无论如何,硬件始终在比我们编程关心的更原始的水平上运行。将我们的 Lisp 程序转换为“机器”程序的过程本身就是我们编程的抽象模型。它们的研究和创建为我们提供了对与编程任意模型相关的组织程序的深刻洞察。当然,计算机本身也可以被建模。想想看:最小的物理开关元件的行为是由量子力学建模的,由微分方程描述,其详细行为由在计算机上执行的数值逼近所捕捉,而这些计算机由…组成!
将三个焦点分开识别并不仅仅是战术上的便利。尽管人们说,一切都在头脑中,但这种逻辑上的分离会加速这些焦点之间的符号交流,其丰富性、活力和潜力仅被人类经验中的生命演化所超越。在最好的情况下,焦点之间的关系是亚稳定的。计算机永远不够大,也不够快。硬件技术的每一次突破都会导致更大规模的编程项目、新的组织原则和抽象模型的丰富化。每个读者都应该定期问自己“朝着什么目标,朝着什么目标?”但不要问得太频繁,以免错过编程的乐趣,而陷入苦涩哲学的便秘中。
在我们编写的程序中,一些(但从来不够)执行精确的数学功能,比如排序或查找一系列数字的最大值,确定素数,或者找到平方根。我们称这样的程序为算法,对它们的最佳行为已经有很多了解,特别是关于执行时间和数据存储需求这两个重要参数。程序员应该掌握良好的算法和习惯用法。尽管有些程序抵制精确的规范,但程序员有责任估计,并始终努力改进它们的性能。
Lisp 是一个幸存者,已经使用了大约四分之一世纪。在活跃的编程语言中,只有 Fortran 的历史更长。这两种语言都支持重要应用领域的编程需求,Fortran 用于科学和工程计算,Lisp 用于人工智能。这两个领域仍然很重要,他们的程序员对这两种语言如此忠诚,以至于 Lisp 和 Fortran 可能会继续活跃使用至少另一个四分之一世纪。
Lisp 发生了变化。本书中使用的 Scheme 方言已经从原始的 Lisp 发展出来,在几个重要方面与后者不同,包括变量绑定的静态作用域和允许函数产生函数作为值。在其语义结构上,Scheme 与 Algol 60 和早期的 Lisps 有着密切的联系。Algol 60 永远不会再成为一种活跃的语言,它在 Scheme 和 Pascal 的基因中继续存在。很难找到比围绕这两种语言聚集的文化更不同的两种语言。Pascal 用于构建金字塔——由推动沉重的方块到位的军队建造的令人印象深刻、令人叹为观止的静态结构。Lisp 用于构建有机体——由适应波动的无数简单有机体的小队建造的令人印象深刻、令人叹为观止的动态结构。在这两种情况下使用的组织原则是相同的,除了一个非常重要的区别:赋予个体 Lisp 程序员的自由功能远远超过 Pascal 企业中所能找到的。Lisp 程序用功能膨胀的库,其效用超越了产生它们的应用程序。列表,Lisp 的本地数据结构,在很大程度上负责这种效用的增长。列表的简单结构和自然适用性反映在那些令人惊讶地非特异的函数中。在 Pascal 中,可声明的数据结构的丰富多样导致函数内的专业化,抑制和惩罚了随意的合作。最好是有 100 个函数操作一个数据结构,而不是有 10 个函数操作 10 个数据结构。因此,金字塔必须在千年不变;有机体必须进化或灭亡。
为了说明这种差异,比较一下这本书中的材料和练习的处理方式与使用 Pascal 的任何第一课文本中的处理方式。不要误以为这是麻省理工学院专用的教材,只有那里的学生才能理解。这本书必须是一本严肃的 Lisp 编程书,不管学生是谁,它在哪里使用。
请注意,这是一本关于编程的书,不同于大多数 Lisp 书籍,后者被用作人工智能工作的准备。毕竟,软件工程和人工智能的关键编程问题在研究的系统变得更大时往往会融合在一起。这解释了为什么人工智能之外对 Lisp 的兴趣越来越大。
正如人们所期望的那样,人工智能研究产生了许多重要的编程问题。在其他编程文化中,这些问题的激增导致了新语言的产生。事实上,在任何非常大的编程任务中,一个有用的组织原则是通过发明语言来控制和隔离任务模块内的流量。随着接近我们人类最常互动的系统边界,这些语言往往变得不那么原始。因此,这些系统包含了许多次复制的复杂语言处理功能。Lisp 具有如此简单的语法和语义,以至于解析可以被视为一项基本任务。因此,解析技术在 Lisp 程序中几乎不起作用,语言处理器的构建很少成为大型 Lisp 系统增长和变化速度的障碍。最后,正是这种语法和语义的简单性造成了所有 Lisp 程序员所承担的负担和自由。任何规模超过几行的 Lisp 程序都无法在没有自由功能的情况下编写。发明和适应;发作和重新发明!我们向在括号的巢穴中书写他的思想的 Lisp 程序员干杯。
—Alan J. Perlis,康涅狄格州纽黑文
原文:Preface
译者:飞龙
《计算机程序的构造和解释》(SICP)通过建立一系列计算的心智模型,向读者介绍了计算的核心思想。第 1-3 章涵盖了所有现代高级编程语言共有的编程概念。SICP 的最初两版使用 Scheme 编程语言作为其程序示例,其极简的表达式导向语法使得该书能够专注于基本思想而不是所选择语言的设计。第 4 和 5 章使用 Scheme 来制定 Scheme 的语言处理器,加深读者对心智模型的理解,并探索语言扩展和替代方案。
自 1984 年出版以及 1996 年的第二版以来,SICP 已被世界各地的大学和学院采用为教材,包括新加坡国立大学(NUS),该校于 1997 年引入了基于 SICP 的入门课程 CS1101S。在 1990 年代中期,Python、JavaScript 和 Ruby 等语言出现,它们与 Scheme 共享核心设计元素,但采用了更复杂的面向语句的语法,使用熟悉的代数(中缀)表示法。它们的流行崛起导致教师们调整了基于 SICP 的课程,通常是通过将示例程序翻译为他们选择的语言,添加特定于该语言的材料,并省略第 4 和 5 章的材料。
对将第二版 SICP 改编为 JavaScript(SICP JS)的工作始于 2008 年的 NUS,CS1101S 于 2012 年转换为 JavaScript。语言标准 ECMAScript 2015 引入了 lambda 表达式、尾递归和块作用域变量和常量,这使得改编变得非常接近原版。我们只在 JavaScript 和 Scheme 之间的差异迫使我们感到必须进行实质性的改变时才对 SICP 进行了重大改动。该书只涵盖了 JavaScript 的一小部分,因此读者不应该使用它来学习这种语言。例如,JavaScript 对象的概念——按任何标准来看都是其基本组成部分之一——甚至没有提到!
通过添加模拟 Scheme 基元的库(包括对列表结构的支持),并相应地调整文本,将第 1-3 章的程序翻译成 JavaScript 是直截了当的。然而,转换到 JavaScript 迫使我们对第 4 和 5 章的解释器和编译器进行微妙的更改,以处理返回语句。Scheme 的表达式导向语法没有返回语句,而返回语句是面向语句的语言的一个显著特征。
通过使用 JavaScript,第 1-3 章向读者介绍了大多数主流语言的语法风格。然而,同样的语法风格在第 4 章中引起了重大变化,因为程序的直接表示作为数据结构不再能够被视为理所当然。这为我们提供了一个机会,在第 4.1 节中向读者介绍程序解析的概念,这是编程语言处理器的重要组成部分。在第 4.4 节中,JavaScript 的严格语法结构使得所呈现的逻辑编程系统的实现变得复杂,并暴露了 JavaScript 作为编程语言设计工具的局限性。
SICP JS 的 MIT Press 网页链接到了这本书的用户支持。这提供了书中的所有程序和丰富的教师资源,包括大量额外的练习和关于在典型大学学期中可以涵盖的 SICP JS 子集的建议。该书中的 JavaScript 程序在符合 ECMAScript 2020 JavaScript 规范(ECMA 2020)的任何 JavaScript 解释器中以推荐的严格模式运行。MIT Press 网页包括 JavaScript 包sicp
,其中提供了书中被认为是“原始”的所有 JavaScript 函数。
我们真诚地希望,如果这本书是你第一次接触编程,你将利用你对计算机程序的结构和解释的新理解来学习更多的编程语言,包括 Scheme 和完整的 JavaScript 语言。如果你在阅读 SICP JS 之前已经学过 JavaScript,你可能会对支撑该语言的基本概念有新的见解,并发现如此少的东西可以取得如此大的成就。如果你已经了解原始的 SICP,那么你可能会喜欢看到熟悉的思想以新的形式呈现,并且可能会欣赏书的网页上提供的在线比较版本,其中 SICP JS 和 SICP 可以并排查看。
—Martin Henz 和 Tobias Wrigstad
原文:Prefaces to Structure and Interpretation of Computer Programs, 1996 & 1984
译者:飞龙
软件可能不像其他任何东西,它可能是用来丢弃的:整个重点是始终将其视为肥皂泡吗?
——艾伦·J·佩里斯
自 1980 年以来,本书的内容一直是麻省理工学院入门级计算机科学课程的基础。在第一版出版时,我们已经教授了这些内容四年,直到第二版出现为止,又过去了十二年。我们很高兴看到我们的工作被广泛采用并融入其他教材中。我们看到我们的学生将本书中的思想和程序作为新计算机系统和语言的核心进行构建。在对古代犹太文学的双关语的实际实现中,我们的学生已经成为我们的建设者。我们很幸运有这样有能力的学生和有成就的建设者。
在准备这个版本时,我们吸收了数百个澄清建议,这些建议来自我们自己的教学经验以及麻省理工学院和其他地方的同事的评论。我们重新设计了书中大部分主要的编程系统,包括通用算术系统、解释器、寄存器机模拟器和编译器;我们重新编写了所有的程序示例,以确保符合 IEEE Scheme 标准(IEEE 1990)的任何 Scheme 实现都能运行这些代码。
这个版本强调了几个新主题。其中最重要的是不同时间处理方法在计算模型中所起的核心作用:具有状态的对象、并发编程、函数式编程、惰性求值和非确定性编程。我们增加了关于并发和非确定性的新章节,并试图贯穿整本书的主题。
该书的第一版紧随我们麻省理工学院一个学期的课程大纲。随着第二版中的所有新材料,不可能在一个学期内覆盖所有内容,因此教师将不得不进行选择。在我们自己的教学中,我们有时会跳过逻辑编程部分(第 4.4 节),我们让学生使用寄存器机模拟器,但我们不涉及其实现(第 5.2 节),我们只对编译器进行了简要概述(第 5.5 节)。即便如此,这仍然是一门密集的课程。一些教师可能只希望覆盖前三四章,将其他材料留给后续课程。
麻省理工学院出版社的网站为本书的用户提供支持。这包括书中的程序、示例编程作业、补充材料以及 Lisp 方言 Scheme 的可下载实现。
—哈罗德·阿贝尔森和杰拉尔德·杰伊·萨斯曼
计算机就像小提琴。你可以想象一个新手先尝试留声机,然后再尝试小提琴。他说后者听起来很糟糕。这就是我们从人文学家和大多数计算机科学家那里听到的论点。他们说,计算机程序对特定目的很好,但不够灵活。小提琴和打字机也是如此,直到你学会如何使用它们。
——马文·明斯基,“为什么编程是表达不清楚和粗糙构想的好媒介”
《计算机程序的结构与解释》是麻省理工学院计算机科学的入门课程。对于主修电气工程或计算机科学的麻省理工学院学生来说,这是“通识核心课程”的一部分,该核心课程还包括两门有关电路和线性系统的课程以及一门关于数字系统设计的课程。自 1978 年以来,我们一直参与该课程的开发,并自 1980 年秋季起以目前的形式教授该课程,每年约有 600 至 700 名学生。大多数学生在计算方面几乎没有或根本没有正式的训练,尽管许多人稍微接触过计算机,有些人有丰富的编程或硬件设计经验。
我们设计这门计算机科学入门课程的两个主要关注点。首先,我们希望确立一个观念,即计算机语言不仅仅是让计算机执行操作的一种方式,而是一种表达方法论思想的新颖形式。因此,程序必须是为人们而写的,只是偶然为机器执行。其次,我们认为在这个层次上需要解决的基本问题不是特定编程语言结构的语法,也不是计算特定函数的巧妙算法,甚至不是算法的数学分析和计算基础,而是控制大型软件系统的知识复杂性的技术。
我们的目标是,完成这门课程的学生应该对编程的风格和美学元素有所了解。他们应该掌握大型系统中控制复杂性的主要技术。如果程序以示范的风格编写,他们应该能够阅读长达 50 页的程序。他们应该知道什么不需要阅读,以及在任何时候他们不需要理解什么。他们应该对修改程序感到自信,保留原作者的精神和风格。
这些技能并不是计算机编程所特有的。我们教授和借鉴的技术是所有工程设计的共同之处。我们通过构建隐藏细节的抽象来控制复杂性。我们通过建立常规接口来控制复杂性,这使我们能够以“混搭”的方式组合标准、理解透彻的部件来构建系统。我们通过建立描述设计的新语言来控制复杂性,每种语言都强调设计的特定方面并淡化其他方面。
我们对这门课程的方法的基础是我们的信念,即“计算机科学”并不是一门科学,它的重要性与计算机无关。计算机革命是我们思考方式和表达思想方式的革命。这种变化的本质是最好称为“程序式认识论”的出现——从命令的角度研究知识结构,而不是古典数学学科所采用的更为陈述性的观点。数学提供了处理“是什么”的概念的精确框架。计算提供了处理“如何”的概念的精确框架。
在教授我们的材料时,我们使用的是 Lisp 编程语言的一个方言。我们从不正式教授这种语言,因为我们不必这样做。我们只是使用它,学生们几天就能掌握。这是 Lisp 类语言的一个巨大优势:它们几乎没有形成复合表达式的方式,几乎没有语法结构。所有的形式属性都可以在一个小时内覆盖,就像国际象棋的规则一样。过了一段时间,我们就会忘记语言的语法细节(因为几乎没有),并着手处理真正的问题——弄清楚我们想要计算什么,如何将问题分解为可管理的部分,以及如何处理这些部分。Lisp 的另一个优势是,它支持(但不强制)更多的程序模块化分解的大规模策略,比我们所知道的任何其他语言都要多。我们可以进行过程和数据抽象,我们可以使用高阶函数来捕捉常见的使用模式,我们可以使用赋值和数据变异来模拟局部状态,我们可以使用流和延迟求值来链接程序的部分,我们可以轻松实现嵌入式语言。所有这些都嵌入在一个交互式环境中,具有出色的支持,用于增量程序设计、构建、测试和调试。我们感谢所有世代的 Lisp 巫师,从 John McCarthy 开始,他们打造了一种前所未有的强大而优雅的工具。
Scheme,我们使用的 Lisp 方言,是试图将 Lisp 和 Algol 的力量和优雅结合在一起的尝试。从 Lisp 中,我们获得了源自简单语法、将程序统一表示为数据对象以及垃圾回收堆分配数据的元语言能力。从 Algol 中,我们获得了词法作用域和块结构,这些都是编程语言设计先驱在 Algol 委员会上的贡献。我们希望引用 John Reynolds 和 Peter Landin 对 Church 的λ演算与编程语言结构关系的洞察。我们也承认我们对数学家的债务,他们在计算机出现之前就已经勘探了这个领域。这些先驱包括 Alonzo Church,Barkley Rosser,Stephen Kleene 和 Haskell Curry。
—Harold Abelson 和 Gerald Jay Sussman
译者:飞龙
《计算机程序的构造和解释》(SICP JS)的 JavaScript 改编是在新加坡国立大学(NUS)为 CS1101S 课程开发的。该课程由 Low Kok Lim 共同教授了六年,他的良好教学判断对课程和这个项目的成功至关重要。CS1101S 教学团队包括许多 NUS 同事和 300 多名本科生助教。他们在过去九年中不断的反馈使我们解决了无数 JavaScript 特定的问题,消除了不必要的复杂性,同时保留了 SICP 和 JavaScript 的基本特征。
SICP JS 是一个软件项目,除了书籍项目。我们在 2008 年从原作者那里获得了书籍来源。早期的 SICP JS 工具链是由刘航开发的,并由冯飘飘进行了改进。Chan Ger Hean 开发了印刷版的第一批工具,基于这些工具,Jolyn Tan 开发了第一版电子书的工具,何欣悦和王倩重新利用这些工具制作了当前的比较版。Samuel Fang 设计并开发了 SICP JS 的在线版本。
SICP JS 和 CS1101S 的在线版本在很大程度上依赖于一个名为Source Academy的软件系统,以及它支持的 JavaScript 子语言称为Source。在准备 SICP JS 期间,许多学生为 Source Academy 做出了贡献,并且系统将他们 prominently 列为“贡献者”。自 2020 年以来,新加坡国立大学 CS4215 课程的学生们贡献了几种编程语言实现,这些实现被用于 SICP JS:在第 3.4 节中使用的并发版本是由 Zhengqun Koo 和 Jonathan Chan 开发的;在第 4.2 节中使用的惰性实现是由 Jellouli Ahmed,Ian Kendall Duncan,Cruz Jomari Evangelista 和 Alden Tan 开发的;在第 4.3 节中使用的非确定性实现是由 Arsalan Cheema 和 Anubhav 开发的;Daryl Tan 帮助将这些实现整合到了 Academy 中。
我们感谢 STINT,瑞典国际合作研究和高等教育基金会,他们的休假计划使 Martin 和 Tobias 联系在一起,并允许 Tobias 成为 CS1101S 的联合教师,并加入 SICP JS 项目。
我们要感谢 ECMAScript 2015 委员会的勇敢工作,由 Allen Wirfs-Brock 领导。SICP JS 在很大程度上依赖于 ECMAScript 2015 中的常量和 let 声明以及 lambda 表达式,这些都是在 JavaScript 中添加的。这些增加使我们能够在 SICP 的最重要思想的呈现上保持与原版的接近。Guy Lewis Steele Jr.领导了第一个 ECMAScript 标准化,并对第 4 章的一些练习提供了详细和有用的反馈。
—Martin Henz 和 Tobias Wrigstad
我们要感谢许多人帮助我们开发这本书和这个课程。
我们的主题是“6.231”的明显的知识传承,这是 MIT 在 20 世纪 60 年代末由 Jack Wozencraft 和 Arthur Evans Jr.教授的关于编程语言学和 lambda 演算的精彩课程。
我们对 Robert Fano 深表感谢,他重新组织了麻省理工学院的电气工程和计算机科学的入门课程,强调了工程设计原则。他带领我们开始了这个事业,并写下了这本书演变而来的第一套课程笔记。
我们试图教授的许多编程风格和美学是与 Guy Lewis Steele Jr.一起发展的,他与 Gerald Jay Sussman 一起初步开发了 Scheme 语言。此外,David Turner,Peter Henderson,Dan Friedman,David Wise 和 Will Clinger 教会了我们许多出现在这本书中的函数式编程社区的技术。
Joel Moses 教会了我们如何构建大型系统。他在符号计算系统 Macsyma 方面的经验提供了一个洞察,即应该避免控制的复杂性,而是集中于组织数据以反映被建模的世界的真实结构。
Marvin Minsky 和 Seymour Papert 塑造了我们对编程及其在我们的智力生活中的地位的许多态度。我们应该感谢他们,因为他们让我们明白,计算提供了一种表达的手段,可以用来探索那些否则太复杂以至无法精确处理的想法。他们强调,学生编写和修改程序的能力提供了一个强大的媒介,其中探索成为一种自然的活动。
我们也强烈同意 Alan Perlis 的观点,即编程非常有趣,我们最好小心支持编程的乐趣。这种乐趣的一部分来自观察伟大大师的工作。我们很幸运能够在 Bill Gosper 和 Richard Greenblatt 的指导下成为学徒程序员。
很难确定所有为我们课程的发展做出贡献的人。我们感谢所有在过去十五年中与我们一起工作并在我们的课程上投入了许多额外时间的讲师、辅导员和导师,特别是 Bill Siebert,Albert Meyer,Joe Stoy,Randy Davis,Louis Braida,Eric Grimson,Rod Brooks,Lynn Stein 和 Peter Szolovits。我们特别要感谢 Franklyn Turbak 在韦尔斯利的杰出教学贡献;他在本科教学方面的工作树立了一个我们都可以向往的标准。我们感谢 Jerry Saltzer 和 Jim Miller 帮助我们解决并发性的奥秘,以及 Peter Szolovits 和 David McAllester 对第 4 章中非确定性求值的阐述所做出的贡献。
许多人在其他大学中付出了大量的努力来呈现这些材料。我们与之密切合作的一些人包括以色列理工学院的 Jacob Katzenelson,加州大学欧文分校的 Hardy Mayer,牛津大学的 Joe Stoy,普渡大学的 Elisha Sacks,以及挪威科技大学的 Jan Komorowski。我们为我们的同事感到异常自豪,他们在其他大学对这门课程进行了改编,并因此获得了重要的教学奖项,包括耶鲁大学的 Kenneth Yip,加州大学伯克利分校的 Brian Harvey,以及康奈尔大学的 Dan Huttenlocher。
Al Moyé安排我们向惠普工程师教授这些材料,并制作了这些讲座的录像带。我们要感谢才华横溢的教师,特别是 Jim Miller,Bill Siebert 和 Mike Eisenberg,他们设计了包含这些录像带的继续教育课程,并在世界各地的大学和工业界教授了这些课程。
许多其他国家的教育工作者已经付出了大量的工作来翻译第一版。Michel Briand,Pierre Chamard 和 André Pic 出版了法文版;Susanne Daniels-Herold 出版了德文版;Fumio Motoyoshi 出版了日文版。我们不知道谁出版了中文版,但我们认为被选为“未经授权”翻译的对象是一种荣幸。
很难列举所有为我们用于教学目的的 Scheme 系统的技术贡献者。除了 Guy Steele,主要的专家还包括 Chris Hanson,Joe Bowbeer,Jim Miller,Guillermo Rozas 和 Stephen Adams。其他付出大量时间的人包括 Richard Stallman,Alan Bawden,Kent Pitman,Jon Taft,Neil Mayle,John Lamping,Gwyn Osnos,Tracy Larrabee,George Carrette,Soma Chaudhuri,Bill Chiarchiaro,Steven Kirsch,Leigh Klotz,Wayne Noss,Todd Cass,Patrick O’Donnell,Kevin Theobald,Daniel Weise,Kenneth Sinclair,Anthony Courtemanche,Henry M. Wu,Andrew Berlin 和 Ruth Shyu。
除了麻省理工学院的实施,我们还要感谢许多人为 IEEE Scheme 标准工作的贡献,包括编辑 R?RS 的威廉·克林格和乔纳森·里斯,以及克里斯·海恩斯、大卫·巴特利、克里斯·汉森和吉姆·米勒,他们准备了 IEEE 标准。
丹·弗里德曼一直是 Scheme 社区的领导者。社区的更广泛工作不仅涉及语言设计问题,还包括基于 Schemer’s Inc.的 EdScheme 的高中课程和迈克·艾森伯格以及布莱恩·哈维和马修·赖特的精彩书籍等重大教育创新。
我们感谢那些为使这本书成为现实的人的工作,特别是麻省理工学院出版社的特里·埃林、拉里·科恩和保罗·贝斯。埃拉·马泽尔找到了精美的封面图片。对于第二版,我们特别感谢伯纳德和埃拉·马泽尔在书籍设计方面的帮助,以及大卫·琼斯,这位卓越的巫师。我们还要感谢那些对新草案提出深刻评论的读者:雅各布·卡岑尔森、哈迪·迈耶、吉姆·米勒,特别是布莱恩·哈维,他对这本书的做法就像朱莉对他的书《简单方案》所做的那样。
最后,我们要感谢多年来鼓励这项工作的组织的支持,包括来自惠普公司的支持,由艾拉·戈德斯坦和乔尔·伯恩鲍姆实现,以及来自 DARPA 的支持,由鲍勃·卡恩实现。
——哈罗德·阿贝尔森和杰拉尔德·杰伊·萨斯曼
《计算机程序的构造和解释》
JavaScript 版本