你所掌握的,也是我认为并希望的,也就是智慧:那种看到这一机器比你第一次站在它面前时能做得更多的能力,这样你才能将它向前推进。
我认为,在计算机科学中保持计算中的趣味性是特别重要的事情。这一学科在起步时饱含着趣味性。当然,那些付钱的客户们时常觉得受了骗。一段时间之后,我们开始严肃地看待他们的抱怨。我们开始感受到,自己真的像是要负起成功地、无差错地、完美地使用这些机器的责任。我不认为我们可以做到这些。我认为我们的责任是去拓展这一领域,将其发展到新的方向,并在自己的家里保持趣味性。我希望计算机科学的领域绝不要丧失其趣味意识。最重要的是,我希望我们不要变成传道士,不要认为你是兜售圣经的人,世界上这种人已经太多了。你所知道的有关计算的东西,其他人也都能学到。绝不要认为似乎成功计算的钥匙就掌握在你的手里。你所掌握的,也是我认为并希望的,也就是智慧:那种看到这一机器比你第一次站在它面前时能做得更多的能力,这样你才能将它向前推进。 - Alan J. Perils
序
解决大规模问题需要经过一系列规划,其中的大部分东西只有在工作进程中才能做出来,这些规划中充满着与手头问题的特殊性相关的情况。
程序员们必须同时追求具体部分的完美和汇合的适宜性。
本书要讨论的各种问题都牵涉到三类需要关注的对象:人的大脑、计算机程序的集合以及计算机本身。每一个计算机程序都是现实中的或者精神中的某个过程的一个模型,通过人的头脑孵化出来。这些过程出现在人们的经验或者思维之中,数量上数不胜数,详情琐碎繁杂,任何时候人们都只能部分理解它们。
计算机程序设计领域之令人兴奋的源泉,就在于它所引起连绵不绝的发现,在我们的头脑之中,在由程序所表达的计算机制之中,以及在由此所导致的认识爆炸之中。如果说艺术解释了我们的梦想,那么计算机就是以程序的名义执行着它们。
在我们写出的程序里,有些程序执行了某个精确的数学函数(但是绝不够精确),例如排序,或者找出一系列数中的最大元。我们将这种程序称为算法,关于它们的最佳行为已经有了许多认识,特别是关于两个重要的参数:执行的时间和对数据存储的需求。程序员应该追求好的算法核惯用法。即使某些程序难以精确地描述,程序员也有责任去估计它们的性能,并要继续设法去改进之。
我们希望建立起一种看法:一个计算机语言并不仅仅是让计算机去执行操作的一种方式,更重要的,它是一种表述有关方法学的思想的新颖的形式化媒介。因此,程序必须写得能够供人们阅读,偶尔地去供计算机执行。其次,我们相信,在这一层次的课程里,最基本的材料并不是特定程序设计语言的语法,不是有效计算某种功能的巧妙算法,也不是算法的数学分析或者计算的本质基础,而是一些能够用于控制大型软件系统的智力复杂性的技术。
这些技能并不仅仅适用于计算机程序设计。我们所教授和提炼出来的这些技术,对于所有的工程设计都是通用的。我们在适当的时候隐藏起一些细节,通过创建抽象去控制复杂性。我们通过建立约定的界面,以便能以一种『混合与匹配』的方式组合起一些标准的、已经很好理解的片段,去控制复杂性。我们通过建立一些新的语言去描述各种设计,每种语言强调设计中的一个特定方面并降低其他方面的重要性,以控制复杂性。
『计算机科学』并不是一种科学,而且其重要性也与计算机本身并无太大关系。计算机革命是有关我们如何去思考的方式,以及我们如何去表达自己的思考的一个革命。在这个变化里最基本的东西,就是出现了这样一种或许最好是称为过程性认识论的现象——这就是如何从一种命令式的观点去研究知识的结构,这一观点是与经典数学领域中所采用的更具说明性的观点完全不同的。数学为精确处理『是什么』提供了一种框架,而计算则为精确处理『怎样做』的概念提供了一种框架。
构造过程抽象
心智的活动,除了尽力产生各种简单的认识之外,主要表现在如下三个方面:1)将若干简单认识组合为一个复合认识,由此产生出各种复杂的认识。2)将两个认识放在一起对照,不管它们如何简单或者复杂,在这样做时并不将它们合而为一。由此得到有关它们的相互关系的认识。3)将有关认识与那些在实际中核它们同在的所有其他认识隔离开,这就是抽象,所有具有普遍性的认识都是这样得到的。 - John Locke, 有关人类理解的随笔 1690
我们准备学习的是有关计算过程的知识。计算过程是存在于计算机里的一类抽象事物,在其演化过程中,这些过程会去操作一些被称为数据的抽象事物。人们创建出一些称为程序的规则模式,以指导这类过程的进行。
程序设计的基本元素
一个强有力的程序设计预研,不仅是一种指挥计算机执行任务的方式,它还应该成为一种框架,使我们能够在其中组织自己有关计算过程的思想。每一种强有力的语言都为此提供了三种机制:
- 基本表达形式,用于表示语言所关心的最简单的隔离
- 组合的方法,通过它们可以从较简单的东西出发构造出复合的元素
- 抽象的方法,通过它们可以为复合对象命名,并将它们当做单元去操作
在程序设计中,我们需要处理两类要素:过程和数据。非形式地说,数据是一种我们希望去操作的『东西』,而过程就是有关操作这些数据的规则的描述。这样,任何强有力的程序设计语言都必须能够表述基本的数据和基本的过程,还需要提供对过程和数据进行组合和抽象的方法。
程序设计语言中一个必不可少的方面,就是它需要提供一种通过名字去使用计算对象的方式。我们将名字标识符称为变量,它的值也就是它所对应的那个对象。
实际上,构造一个复杂的程序,也就是为了去一步步地创建出越来越复杂的计算性对象。解释器使这种逐步的程序构造过程变得非常方便,因为我们可以通过一系列交互式动作,逐步创建起所需要的名字-对象关联。这种特征鼓励人们采用递增的方式去开发和调试程序。在很大程度上,这一情况也出于另一个事实,那就是,一个 Lisp 程序通常总是由一大批相对简单的过程组成的。