最近在看The Little Schemer,似乎整本书最难的部分就是它字最多的部分──前言。玩笑话,这本书的文字很简单,但内容很有深度。我试着翻译了一下(翻译果然是个苦差事)它“最难的部分”
前言
为了庆祝Scheme诞生二十周年,我们第三次修订了The Little LISPer,并将其改名为The Litlle Schemer,这样更准确一些。我们还写了它的续篇:The Seasoned Schemer。
一个程序接收数据,处理数据,输出数据。要设计一个程序需要对数据有着透彻的理解。一个优秀的程序同时也能反映它要处理的数据是什么样子。很多数据集,也包括很多程序,都是递归形式的。递归是一种用自己来定义自己或依靠自己来求解自己的方式。
本书的目的就是要教你用递归来思考。我们遇到的第一个问题是采用什么语言来阐述这个概念,有三个选择:自然语言,比如英语;数学表达;或者程序语言。模棱两可,不够准确,甚至冗长,这些在日常交流中自然语言的优势,在介绍递归这样精细的概念时却变成了劣势。数学表达恰恰相反,它可以用极少的符号表达出一个复杂的概念。遗憾地是,数学表达往往晦涩难懂,只有经过专门的学习才能领会。技术和数学的结合给了我们一个更理想的选择──程序语言。我们相信,程序语言是解释递归这一概念的最佳途径。它继承了数学的简明,但与之不同的是,我们可以运行代码来观察它们的行为,并通过不断地修改来评估这些改动带来的影响。
Scheme也许是最适合教授递归的程序语言了。从Scheme一诞生就支持符号(计算)。程序员不必考虑他自己的语言符号和这些符号在计算机中如何表示。Scheme进行计算最根本的方法就是递归。编程最主要的工作就是找出(内在的)递归定义。大多数Scheme实现都采用交互的方式──程序员能立即感知程序的行为并进行调整。当你读完这本书,最大的感触可能是,Scheme程序和它要处理的数据存在着直接的对应。
虽然可以很严谨地讲解Scheme,但理解Scheme并不需要经过专门的数学训练。实际上,The Little Schemer出自一门为期两周的Scheme导论课的授课提纲,这门课是专门为没接触过编程,对数学又没有丝毫兴趣的学生开设的。这些学生大多准备从事公共事务方面的职业。我们的理念是“用Scheme递归地写出程序,关键是要识别出模式”。我们关注的是如何用递归的方式编程,因此我们只需用到Scheme中很小的一部分功能:car, cdr, cons, eq?, null?, zero?, add1, sub1, number?, and, or, quote, lambda, define, cond。事实上,这就够了。
The Little Schemer和它的姊妹篇The Seasoned Schemer不会向你介绍怎么完成现实世界中的任务,但掌握这两本书中的概念将有助于你更好地理解计算的本质。
阅读本书之前你必须掌握
读者应能轻松地阅读英语,认识数字,知道计数。
致谢
……
给读者的建议
不要匆匆扫过,请仔细阅读。书里散落着很多有用的提示。试着从不同的侧面去理解。按章节顺序读。如果你还没有完全弄懂这一章,不要进入下一章,不然你会觉得理解起来很吃力。问题的难度是递增的,如果你还不能解决前面的问题,后面的题会更无处下手。
在书中,你和我们会就Scheme编程中一些有趣的问题进行讨论。如果可以,运行你遇到的例子。Scheme程序很容易读懂。虽然在不同的Scheme实现之间有一些细微的语法差别(主要是个别名字的拼写和某些函数的作用域稍有不同),但Scheme基本上是一致的。要运行书中的Scheme代码,你还需要定义atom?, sub1, add1.我们会在书里详细介绍:
要想知道你的Scheme有没有正确地定义atom?,运行(atom? (quote())),看是否返回#f。事实上,这个测试也适用于现代的Lisp版本,如Common Lisp。如果用Lisp,你需要添加atom?函数:
你可能需要稍稍修改这程序。书中提供的例子可能都需要做一些修改。在书中,我会在注释里注明在不同Scheme实现的一些特性。“S:”表示Scheme,“L:”表示Common Lisp。
到第四章,我们会深入探讨三个基本的算术操作:add1, sub1, zero?。虽然Scheme没有提供add1和sub1,但你可以用内置的加减运算符来定义它们。为了避免逻辑上的死结,我们用另一套符号来表示基本的加减操作:+, -(译注:原书中用的是空心的加号和减号)。
我们不会在书中给出任何标准的定义。我们相信你能自己搞出一套来,这样比我们给你一个写好的定义更能让你记住、理解它们。请好好消化十诫和五律(译注:书一开始给出的Laws and Commandments)。学习Scheme的关键是识别模式。十诫就是对你所见过的模式的总结。书一开始会简化一些概念。但随后,它们会被不断扩展、丰富。你应该知道,这本书里的所有东西都是Scheme,Scheme是一门通用编程语言,不是这样一本导论性质的书能讲清的。在你掌握了本书之后,你可以去读Scheme中那些进阶内容的书。那时,你就能轻松地理解那些内容了。
我们编写本书时遵循一些约定。不同类别符号的字型稍有不同。变量和原子操作名用斜体。基本数据,包括数字和真假的表示,用sans serif字体。关键字,比如define, lambda, cond, else, and, or, and quote, 用黑体。如果你要运行书中的程序,可以忽略字型,但要记得看看注解。为了突出字型的作用,注释里的程序都用typewriter字型。第十章以后,我们将忽略字型的区别,因为从那章开始,我们会把代码也看作数据。
最后谈一谈断句。Webster把断句定义为用标准的符号分割句子来使文意更明确的行为。有时,为了表达清楚,我们会使用非常规的断句方式。注意我们不会对左栏的文字(译注:书采用对话形式,分为左右两栏)断句,因为那是程序语言。
我们的例子中经常出现食物的名字,一是因为食物比抽象的符号更好想象(但这不是一本适合在烹饪的时候看的闲书)。我们希望选择食物作为变量名能有助于你的理解;二是我们想注入一丝乐趣。我们知道这门课会让你多么沮丧,我们希望这一点点乐趣能缓解你的焦躁。
准备好了吗。希望你喜欢接下来的挑战。