论真正教授计算机科学的残酷性
拙译,有能力请章节阅读原文
本论的第二部分探讨「计算机代表一种彻底的革新」这一假设所带来的一些科学上和教育上的后果。为了给这个假设具有明确的内涵,我们必须更加精确地理解所谓「彻底」这一形容词的含义。因此,我们将在本论的第一部分提供与之相关的论据来支撑起我们的假设。
今天我们为明天做计划时,常常使用的是昨天的词汇。我们这样做,因为我们总是喜欢从我们熟悉的、已经在过去验证了的概念出发。当然,这些旧的词汇和概念可能不是非常适合新的情景,因为我们的未来与过去不同。但是无伤大雅,因为我们随后会延伸这些词汇和概念的意义,使之吻合新的情景。这样一来,词的意义就会随时间而演变,语言学家很熟悉这一现象,也知道这是一个缓慢而渐进的过程。
隐喻和类比,是最常见的认识新事物的方式。通过隐喻和类比,我们能将新事物与旧事物联系起来,将新的事物与熟悉的事物联系起来。如果新发生的变化是足够缓慢而渐进的,那么这种方法的效果是很不错的。然而,在变化剧烈而离散的情况下,这种方式就失去效力了:尽管我们依然冠以「常识」之名,但我们过去的经验已经不再适用,类比变得过于肤浅,隐喻也变得更具误导性而非启发性。这就是「彻底」革新的特征之一,在于变化是急剧的、阶梯性的、离散的。
应对彻底的革新需要一种正交的方法。一个人必须把自己过去从成功中收集的经验、从不幸中培养的习惯统统放弃,以一张白纸般的心态去认识彻底的革新,忍住将它们与熟悉事物联系起来的冲动,因为已有经验已不足以囊括它。同时他也必须以一种全新的态度、把彻底的革新作为完全独立的主题对待。与一种彻底的革新打交道,就像是创造和学习一门无法翻译成母语的新语言。(学习过量子力学的人会明白我的意思)不用说,适应彻底的革新不是一门轻松的活动,它需要付出大量的努力。正因为如此,彻底的革新本身不怎么受人们欢迎。
现在,你可能会问,为什么我要在这样一些简单而明确的概念(即「彻底的革新」)上花这么多篇幅。我的理由很简单:彻底的革新实际上是如此地令人不安,以至于人们往往选择对其视而不见,甚至是拒绝承认它们普遍存在的可能性。
简单谈谈历史上类似的故事吧。人称数学王子的高斯,在这件事上也难免展现出一丝懦弱:他雪藏了自己对于非欧几何的发现——不知道他是否回想起了伽利略的命运,也不知道他是否已经接受了未来爱因斯坦对此的微词——让后来研究者波莱和洛巴切夫斯基去替他接受抨击。另一个例子可能会更有启发性,我们再往前追溯到中世纪:「类比推理」思维的盛行是那个时代的一个重要的特征,与此同时另一个重要的特征则是思想发展的完全停滞……两者同时出现绝不是什么巧合。这里的论断是想要指出:学会敏锐地识破毫无依据的类比,我们就能找到藏在我们时代中的许多中世纪的思维方式。
有一点我必须一提,许多的人认为渐进式变革是历史的唯一路径。当我仔细研究这一事实时,我注意到这部分人的数量比我预想的要多得多。
例如,数学界的绝大多数人从未尝试挑战过它的默许假设,即:研究数学将会是一项与以前一样的思维活动:新课题将会出现、将会发展、然后过时,但是,人类大脑的活动不会有多大变化,一直以来讲解数学、学习数学、理解数学、解决问题和作出数学发现的方式将会保持不变。赫伯特·罗宾斯认为数学能力不可能有巨大飞跃,他清楚地阐述了自己的理由:
「无论在训练和器材上投入多少努力,没有人能在5秒内跑完100米。同样的道理也适用于大脑的活动。现在的人脑和五千年前的人脑没有什么不同。做数学研究也是如此,你必须认识到人类思维能力的运用早已达到极限。」
我在页面空白处的评论是:「所以应该少浪费脑细胞,多用机器计算!」我们可以用罗宾斯的类比来反驳他自己:想要快速地从A点到B点,如今有比跑步更快的方法——罗宾斯以「做数学研究」为由拒绝承认的其他可以替代大脑机能的方法——我们只需简单地调整一下定义以适应他的需要:从定义上看,数学依然是过去的样子。关于数学家的讨论就说到这吧。
让我再举一个例子说明人们普遍不相信「彻底的革新」的存在,因此更需要学习如何应对它们。现在流行一种教育方法论,它认为渐进地、潜移默化地教授是唯一值得提倡的模式。有多少文本从教材上被删掉,就因为它们依赖学生的直觉!它们不断地把新生事物尽可能地以人们已经熟悉的面貌呈现出来,它们刻意地把新教材与学生熟悉的世界联系起来。数学教学中经常能发现这种方法论的影子:「2+3=5」中的那个可怕算术运算符「加」被小心化装成「和」,首先学生们会接触到许多熟悉的例子,例如看得见摸得着的苹果和梨,它们会被考虑在内,而其他一些同样可计数的事物比如百分数和电子,则被排除在外。这种愚蠢传统也反映在大学为未来的物理学家、建筑师和商业专家所准备的各不相同的微积分入门课程中,每门课程都用各自领域里精心准备的例子装饰起来。这里体现的教育教条似乎是:只要别让学生注意到他在学习一些全然陌生的东西就好;学生的第一感觉往往是正确的。我认为这种教育方法的一个严重的失误是:没有让下一代为应对「彻底的革新」而做好准备。[当费迪南德国王访问塞维利亚大学时,校长骄傲地安抚国王说:「陛下,别异想天开的思想不会出现在这里。」随后一个世纪里西班牙遭遇到的问题证明,我将这一缺陷描述为「严重」是有理由的。]关于渐进式教育模式的讨论就说到这吧。
「彻底的革新」的概念具有现代性的意味,因为尽管我们没有做好应对它们的准备,但科学和技术已将改变强加于我们,深刻地改变了我们的生活。早期在科学上的例子是相对论和量子力学;后来在技术上的例子是原子弹和药物。几十年来,前两者(相对论和量子力学)引发了宗教、哲学或其他准科学领域的研究浪潮。而对于后两者(原子弹和药物),我们每天都可以注意到,无论是政治家与宗教领袖还是普罗大众,对它们的认识和处理方式都还有着严重的不足。关于「彻底的革新」损害我们心灵平静的讨论就说到这吧。
我援引出这些论证,是为了接下来的论点:自动计算机代表了这样一种「彻底的革新」,只有认识到这一点,我们才能识破围绕着它们的所有废话、误解和神话。现在的情况甚至比这还要严峻,仔细观察就会发现,自动计算机不仅仅在单一方面,而是在两个方面表现为一种「彻底的革新」。
第一处「彻底的革新」是现在计算设备强大算力的一个直接后果。我们往往这样应对庞大而复杂的事物:分而治之,即把整体看作是部分的组合,并分别处理部分。首先,如果分解后的部分依然庞大而复杂,那么我们就重复这个步骤。城市由街道构成,街道由建筑物构成,建筑物由墙和地板构成,墙和地板又由砖砌成……这样的划分最终下降到基本粒子。然后,我们在每一个层次上,把问题交由各个层次的专家来解决:从城市规划师,到建筑师,再到材料物理学家……因为整体在某种意义上比部分要「大」,这种层次分解的深度是关于整体和最小部分的「大小」之比的一个对数。而在自动计算机相关的领域中,从单比特到几百兆字节的存储空间、从一微秒到半小时的计算时间,我们所面对的这个比例是巨大到令人难以想象的109!这样,程序员独一无二的学科和专业赋予了他独特的地位——研究在一个如此巨大的尺度上如何以单一的技术来构建整体与部分的桥梁。他必须能够从概念层次的角度思考问题,这种思考的深度比以往任何头脑的思考都困难得多。与这些语义层次的数量相比,一般的数学理论几乎是扁平的。自动计算机这种功能对深层概念层次的需求,向我们发出了一个历史上前所未有的智力挑战。
我必须再次强调这种「彻底的革新」的存在,因为渐进演进的信奉者非常难以认识到这一点。在他看来,自动计算机与我们熟悉的收银机没什么不同,只是更大、更快、更灵活而已。但是这个类比太肤浅了:在交通工具的尺度上,超音速喷气式飞机与一个爬行的婴儿速度上的差距只有1000倍,而计算机与收银机之间的差距要比这要大上许多个数量级。
第二处「彻底的革新」是指这样一个事实:自动计算机是我们历史上第一种大规模数字设备。我们之前就已经有了一些离散的元件:比如我刚才提到的收银机可以增加拥有独立按键的键盘:按一下就可以输入Q或W,尽管这两个键彼此相邻,但是它们独立发挥作用。但这种机制是少数,我们绝大部分的机制都被视为模拟设备,其行为在很大范围内是关于所有参数的连续函数:按压铅笔的力量略大一点,就会得到略微粗一点的线条,拉小提琴的手指略微错位,演奏就会略微变调。顺便一提,我们在认识自身内部的种种机制时,主要也是把自己视为模拟设备的,比如:「如果我们努力一点,就会变得更好一点」。通常,描述这些行为的函数不仅是连续的,还是单调的:为了测试一个锤子是否适用于一定范围所有的钉子,我们只需尝试下在这个范围内最小和最大的钉子,如果都合适,那么我们完全愿意相信这个锤子会适合我们在这两者之间的所有钉子。
将程序看作某种抽象的机械或设备是可能的,甚至是诱人的。然而这样做是非常危险的:这个类比太肤浅了,因为程序的机制与我们所接触过的所有模拟设备完全不同。与所有数字编码的信息一样,它不可避免地具有一个令人不安的特性,即最小的扰动——例如单个位的变化——也可能会带来最严重的后果。[完整起见我做一下补充,引入冗余或错误纠正不会从本质上改变这一点。]在离散计算的世界里,不存在也不会存在一个有意义的指标,使得在这个指标下的「小」变化总是引起「小」效应。
这第二处「彻底的革新」与它的许多同类有着相同的命运:由于它刺痛了人们的乐观心态,它被否认了。我不知道这种否认和怀疑让美国付出了怎样的代价,但每天浪费100万美元相对而言应该是一个保守的估计。
在尽量宽泛地描述了计算机是怎样的一种新事物之后,我现在将证明这种新事物确实是「彻底的」。我将通过解释一系列奇怪的现象来达到这一目的,这些现象简直是全无理智——但正如我们现在所能理解的那样——都不过是人们为了隐藏或否认令人恐惧的陌生事物所作的努力。
许多这样的现象都已捆绑在「软件工程」的名义下,正如经济学被称为「悲惨的科学」,软件工程应该被称为「注定失败的学科」,因为它的目标是自相矛盾而无法趋近的,所以它注定要失败。当然,软件工程将自己包装成一个看似有价值的事业,但那是洗脑:如果你仔细阅读它的文献并分析它的信徒究竟在做什么,你会发现软件工程所默默接受了的宪章——「在不能编程的条件下,如何编程。」。
「软件工程」这一名字如此受欢迎,足以让人感到不对劲。在我们所谓的「原始社会」中有一个普遍的现象:知道一个人的真名就能对他施展魔法。其实我们也同样的习惯:我们接电话后,用的是毫无意义的问候语而不是用名字来打招呼。
我们也没有比原始的迷信高明多少,我们也相信可以通过用一个安全、熟悉而无辜的名字(如「工程学」)来控制某个未知的、狂野的猛兽。但这种控制完全是象征性的,就像美国一家电脑制造商几年前做的那样:该公司通过引入一个将所有程序员训练到高级的简单设备,在一夜之间拥有了数百名「软件工程师」。关于「软件工程」这个术语就说到这吧。
这种做法充斥着一种让人安心的错觉,即程序只是并无特殊之处的普通设备,唯一的区别在于程序的生产可能需要一种新型工匠——程序员。有着这样的误解,他们于是开始以「每月生产的代码行数」来衡量「程序员的生产力」。这是一个非常昂贵的指标,因为它鼓励编写干巴巴的代码,但是现在我没有兴趣去讨论这个指标究竟有多愚蠢——即使是从最纯粹的业务的角度上。我想说的是,在我们统计代码行数时,我们不该把它们视作「生产的行数」,而应视作「消耗的行数」:而当前最流行的统计方式则愚蠢到用错误的计数单位来记账。
除了生产力的概念,质量控制的概念也被这种令人安心的错觉——程序与其他设备别无二致——所扭曲。20年前就有人指出,程序测试可以令人信服地证明程序存在bug,但永远无法证明程序中不存在bug。在虔诚地引用了这句广为流传的箴言之后,软件工程师就把它抛在脑后、回到工作中继续改进他的测试策略——就像从前的炼金术士继续改进他的炼金装置一样。
「软件维护」一词进一步揭示了这种深深的误解,因此许多人仍然认为程序——甚至编程语言本身——都会磨损、折旧。就像你的汽车也需要维修一样。有个著名的故事,讲了一个石油公司认为其PASCAL程序的寿命不会像FORTRAN程序那样长,「因为PASCAL语言已无人维护」。
同样,我必须提请大家注意,令人震惊的是人们已接受了这样一种观点,即软件生产的困难性在很大程度上是由于缺乏适当的「编程工具」。(据说「程序员的工作站」很快就要出现了。)浅显的类比又一次出现,得中世纪思想之大成。「算法动画」这类乏味的「编程工具」,并没有让我的判断力变得更加成熟;相反,它证实了我最初的猜想:我们这是在跟跳大神的打交道。
最后,我要澄清一点,实际上不仅仅是工业界无法面对「彻底的革新」,其他行业也是如此。让我来解释一下人工智能在当下的流行——至少是在美国的流行。有人认为,人类会受到来自「巨型大脑或会思考的机器」的威胁。实际上,如果计算机只是被用来模拟熟悉的平凡的事物,它反倒显得不那么可怕。我确信,这种解释在相当长一段时间内将引起争议,因为人工智能的目标是模仿人类的思维,它经常把自己视为学术前沿,而我的解释则把它降为了后勤。(我觉得使用机器来模仿人类思维相当愚蠢:我宁愿用它们来模仿更好的东西。)
这些证据统统表明,计算机的革新之处确实是「彻底的」。
接下来是我演讲的第二部分也是最难的部分:上述事实在科学上以及教育上引发的后果。当然,对教育的影响比较复杂难以讨论,所以让我们先来谈谈计算机科学本身。计算是什么?计算机科学是关于什么的?
好吧,说到底,计算机能为我们做的唯一一件事就是操作符号并产生相应的结果。根据我们以前的结论,这是一个离散的世界,而且它所涉及的符号的数量和符号操作的数量远超我们的想象:它们的数量大到令人无法想象,大到令人不该去想象。
但是,为了让计算机执行一类有意义的操作(或计算,怎么说都行)之前,我们必须编写一个程序。什么是程序?有几个可能的答案。我们可以把程序看作是一个特殊用途的符号操作器,而且这个符号操作器不需要改变具体的电路连接(该机器与使用问题相关配线板的机器相比,是一个巨大的进步。)我则更愿意用另一种方式来描述程序:程序是一个抽象的符号操作器,它可以通过实现于计算机上来转化为一个具体的符号操作器。毕竟程序的目的已不再是运用我们的机器,正相反,机器的目的是执行我们的程序。
因此,我们的任务就是设计抽象的符号操作器。我们都知道它们是什么样子的:它们看起来像程序,或者用更加笼统的术语说,通常是来自某个形式系统的精心设计的公式。把程序看作一个公式是很有帮助的。首先,它把程序员的任务放在正确的处境上:他必须推导出这个公式。其次,它解释了为什么数学界几乎忽略了来自程序设计的挑战:程序的公式要长而复杂得多,使数学界难以承认它们是公式……现在回到程序员的工作上:他必须推导出那个公式,他必须推导出那个程序。要达到这个目的我们只知道一种可靠的方法:通过符号操作。于是展现在我们面前的是一个闭合的圆:我们通过人工的符号操作,来构造机器的符号操作器。
因此,计算机科学将永远关注机械符号操作和人类符号操作这两个过程之间的相互作用,通常分别称为「计算」和「编程」。认识到这一点有一个直接好处,它揭示了「自动编程」理论在定义上的矛盾。另一个好处是,它为我们提供了一个明确的指示,让我们在科学世界的地图上找到了计算科学的位置:从形式数学和应用逻辑的方向出发,但比它们要延伸到远得多的地方,因为计算科学感兴趣的是如何有效地使用形式化方法,而且其运用的规模要比我们迄今所见的大得多。我建议我们采用计算科学FMI(Formal Methods Initiative,形式方法倡议)这一三字母缩写作为旗帜。
长远来看,我希望通计算机科学能够有效地实现莱布尼茨之梦——以自动符号计算作为人类推理的替代品,从而超越它的父学科,即数学和逻辑。(请注意「替代」与「模仿」之间的区别是:替代方案可以比原有方案更好。)
不用说,这种关于计算科学的愿景没有得到普遍的认同。相反,它遭到了来自各方的广泛反对,有时甚至是强烈反对。以下是一些例子:
- 数学界:他们宁愿继续相信莱布尼茨之梦是一个不切实际的幻想
- 商业界:他们认为计算机将使生活更容易,但还没有准备好在心理上接受计算机只能解决较简单的问题,而且代价是产生复杂得多的问题
- 强迫性程序员亚文化:他信奉这样的教条——一个愚蠢的想法加上一个月疯狂的编码能让他一跃成为百万富翁
- 计算机工程:它宁愿认为一切取决于更高的比特率和更高的每秒钟浮点运算次数
- 军方:他们现在完全沉浸在「利用计算机安全系统来减少数十亿美元的预算」的幻想中
- 所有软科学:对它们而言计算科学是某些跨学科理论的天堂
- 教育界:他们宁愿关闭学习也不愿意向计算机科学专业的学生教授形式数学。
通过编号为6的例子,我不知不觉地也是不可避免地谈到了这个演讲中最棘手的部分:教育上的后果。
教育政策的问题在于,它很少受到与所教授主题相关的科学因素的影响,而几乎完全由科学以外的因素所决定,例如来自学生、家长及未来老板的期望,以及对大学所应扮演的社会角色的普遍看法:是该把重点放在就业,为学生将来步入社会作准备?还是该去教授他们一种知识和态度,让他们在接下来五十年里受益匪浅?我们是勉强地将抽象科学安放在教学中的一个隐蔽地角落?还是坦率承认它们是高科技产业发展不可或缺的动力?即使我们承认后一点,我们又是否能承认这是一门主要从属于形式数学的高科技产业的技术呢?大学是否为在知识上引领着社会的发展?还是说它仅仅提供了社会所要求的技能培训?
传统的学术辞令愿意对这些问题给出令人安心的回答,但我不相信它们。在最近一篇关于「谁统治加拿大?」的文章中,大卫·H·费海提直言不讳地说:「此外,商业精英们相信传统学者与知识分子在很大程度上无关紧要、不值一提。」
因此,如果我透过我那模糊的水晶球去一窥计算机科学教育的未来,我绝对会看到「一切照常」这幅令人沮丧的景象。大学依然缺乏教授硬科学的勇气,将学生引入歧途,每次出现新的幼稚课程都将被视为教育上的进步。
我使用我的水晶球已经有很长的时间了。它的预测总是悲观的,而且往往是正确的,对此我已经习惯了。即便如此,我仍想为你提供一些建议——即使这只是徒劳无功、只让你感到焦虑。
例如,我们可以从清理我们的语言开始,不再将bug称为bug,而是将其称为错误(error)。这样做更加诚实,因为它直接将后果归咎于错误的源头——犯错的程序员。相比之下,「bug会在程序员不注意的时候悄悄潜入」这种灵异故事就不那么诚实了,因为它试图掩盖造就错误的正是程序员自己这个事实。这个简单词汇的变化会有着意想不到的深远影响:此前,只有一个bug的程序通常是「几乎正确的」,这之后,有任何错误的程序则是「不正确的」(因为有错误)。
我的第二个在用词遣句上的建议是用词要更严谨些。这是为了治疗「如果这个人想和那个人说话」综合症(if-this-guy-wants-to-talk-to-that-guy):永远不要用拟人的术语来指代程序或设备的某些部分,也不要让你的学生这样做。这种用词遣句上的改进比你想象的要难得多,你们系可能要考虑对违规者处以罚款,比如本科生罚款二十五美分,研究生罚款五十美分,教员罚款五美元。这样正好,到新制度实行一个学期后,罚款所得资金足够用来发放两个奖学金。
提出这最后一个建议的原因是,拟人化的比喻——可以把它的引入归咎于约翰·冯·诺伊曼——对每个计算科学社区来说都是一个巨大的障碍。现在一些程序会做出这样表述:想要某些数据、知道某些情况、预期某种输入、相信某些前提……这些用词每次都会产生不必要的混淆。这种拟人化背后的类比过于肤浅,它不仅会将人带入歧途,而且会麻痹人的思维。
从某种意义上说,它会将人带入歧途,因为它暗示我们可以用熟悉的、连续的事物(即我们自己)来充分处理陌生的、离散的事物——事实上并非如此。从另一种意义上说,它会麻痹人的思维,由于人是顺时而变的造物,而拟人化比喻迫使人们模仿它的底层计算模型、去从计算行为的角度开始构思程序。这很不好,因为这种对基本操作的推理是对脑力的巨大浪费。
让我向你们解释这种巨大浪费的性质,我会尝试说服你们,说成是「对脑力劳动的巨大浪费」并不夸张。接下来我将以高度技术化的论证来说明这一点,但不要被吓到:这只用到了心算程度的数学。首先,如果我们必须证明一个大集合的所有元素符合某一性质,那么逐个证明这些元素符合该性质的方法是极其低效的:有效的证明不涉及单个元素的属性,而会从该集合的定义出发。
考虑平面图形_Q_,其定义为8×8的正方形,在它的两个对角剪掉两个1×1的正方形。那么_Q_的面积是62,而31个1×2多米诺骨牌的面积同样是62。有这样一个定理:图形_Q_不能被31个这样的多米诺骨牌覆盖。
该定理的另一种表述方式是,如果你从8×8的网格纸开始,每次用一张1×2的多米诺骨牌填充两个相邻格子,那么使用31个多米诺骨牌无论如何将不会正好填满图形_Q_。
所以,一种证明该定理的方法是生成多米诺的所有可能放置,并对每个放置验证它不是图_Q_——这样做非常麻烦。
而一种简单的证明是这样的:把方格纸的格子按照国际象棋棋盘那样涂上黑白两种颜色。每个多米诺骨牌会覆盖两个相邻的格子,即一个白色格子和一个黑色格子,因此,覆盖的白色格子和黑色格子的总数相同。然而,在图_Q_中,白色格子和黑色格子的数量相差2——因为对角的两个格子颜色一定相同——因此没有哪一种覆盖会产生图形_Q_。
上面的简单证明不仅比穷举所有情况的证明要简洁了许多个数量级,而且它本质上也更给强大,因为它对于_Q_的一种泛化情形也适用——任何边长为偶数的矩形。
这就是我的例子。我举这个例子,是因为它简洁地说明了实实在在的数学的力量;毋庸置疑,拒绝利用这种数学力量无异于是智力上和技术上的自杀。这个例子告诉我们的技巧是:如果我们想处理集合的所有元素,那么应该关注集合的定义,而不是集合中的每个具体元素。
回到编程的话题上来。声明一个给定的程序满足某个特定的规范,相当于声明所有在该程序的控制下产生的行为。由于这些行为是由给定的程序定义的,可以应用我们刚刚收获到的技巧:如果我们想处理程序的所有行为,那么应该关注程序的定义,而不是程序的可能的具体行为。我们必须学会在字面上处理程序,并(暂时)忘掉它们需要被解释为可执行代码。
对这个话题还有另一种说法。一门编程语言,由于它具有形式化的语法和语义定义的证明规则,所以它是一个形式系统,程序执行时的行为仅为其提供一个模型。众所周知,形式化系统应该以其本身来处理,而不是以特定的模型来处理。这样可以得到的同样推论,我们应该仅对程序进行推理,而不提它们可能的「行为」。
到此为止,我结束了关于「拟人化是对脑力劳动的巨大浪费」的技术性推理,也同时结束了关于「在计算科学中应该禁止拟人化的比喻」的论证。
不是每个人都能充分理解这一点。最近,我在一门编程入门课上看到了一个伪装成教学软件的演示。这是也一个典型的课程幼稚化的例子,因为它采用了「可视化」的演示工具(它的作者应该因该软件对学生的「蔑视」而载入史册),但接下来它做得更过分:它详尽地列举和演示了在学生的程序的控制下可能出现的行为和特性!这个系统精确地强调了学生必须学会忽略的东西、精准地强化了学生必须遗忘的东西。养成好习惯不难,难的是改掉坏习惯,所以我们必须认为这个系统对大多数学生造成了永久性的精神创伤。
不用说,这个系统完全掩盖了一个重要的事实,即一个程序本身是半个猜想。猜想的另一半是程序应满足的功能规范。程序员的任务就是如同证明定理般,提出这个完整的猜想。
最后,我想请大家思考一下,我们应该如何在编程的入门课程中正确对待作为一种「彻底的革新」的计算科学。
一方面,我们教的东西看起来像谓词演算,但是我们的做法和哲学家有很大区别。为了训练初学者如何操作形式语言,我们应该像布尔代数课那样教授它,使学生熟悉逻辑连接词的所有代数性质。为了进一步切断与直觉的联系,我们应该将布尔域的值{true, false}重命名为{black, white}这样缺乏意义的词。
另一方面,我们应该选择一种简单、干净、命令式的编程语言来教学,使用skip和多重赋值作为基本语句,使用具有局部变量的块结构,作为组合语句的分号运算符,优雅的可选的构造,优雅的迭代结构……如果需要,还可以允许过程调用。
从一开始,贯穿整个课程,我们都在强调:程序员的任务不仅仅是写一个程序,他的主要任务是给出一个形式化的证明,证明他提出的程序与其形式规范相符合。学生被训练着同时设计证明并编写程序,这样他们就有充分的机会来熟悉谓词演算并掌握其符号操作。最后,为了让大家明白这门编程入门课首先是一门形式数学课,我们必须确保所学习的编程语言的具体实现没有出现在教学过程中,这样学生就不会受到测试具体程序的诱惑。以上就是我关于为大学新生开设的编程入门课的建议简述。
这是一个严肃而合理的提议。唯一的缺点是许多人认为它过于激进而无法令人接受。于是我们看到他们编造出一些荒唐的理由来反驳它。我给你举几个例子。
「你不必把这个建议当回事,因为它太荒谬了,戴克斯特拉太脱离现实了。这样是行不通的,我对现实情况太了解了:现实世界的主要问题是你不去用那些卓有成效的解决方法。所以,让我们再试一次老办法。」
「你不必把戴克斯特拉的建议当回事,因为要想教给大学新生这样的教材完全不现实。其实有更简单的方法,你只是把它看得太难了。这样是行不通的,因为这个假设已经被证明是错误的:自80年代初,这种编程入门课每年都为数百名大学新生开设,而且无一例外取得了成功。[据我观察,前面有些句子光说一次是不够的,至少要重复两遍。]所以,让我们再试一次老办法。」
「我勉强赞成你的部分观点,这些教材或许可以用来教育一部分温驯的学生,但是我仍旧拒绝这个提议,因为这样的课程与18岁学生所习惯的课程大相径庭,把这样的课程强加给他们是一种不负责任的行为:这只会令学生感到挫败。不用说,这样是行不通的。」的确,从未操作过形式语言的学生很快就会意识到,他所面对的东西与他以往见过的东西完全不同。但幸运的是,操作这些公式的规则足够少也足够简单,因此用不了多久他便会兴奋地发现——他逐渐开始掌握到一种工具的使用方法,尽管这个工具非常简单,但却给了他一种无比强大的力量,能轻易超越他哪怕最疯狂的梦想。
教导毫无头绪的年轻人有效地使用形式方法是人生的乐趣之一,因为这非常有成就感。在几个月内,他们将带着合理的自信在全新的世界里找到自己全新的方向;在几个月内,他们就已经达到了能够理解这「彻底的革新」的层面。在我看来,这才是教育的意义所在。大学不应该害怕传授「彻底的革新」;相反,他们的使命是鼓励一切做出如此尝试的努力。他们这样的态度是我们反对专治——无论是无产阶级、科学机构还是企业精英——的主要保障。
Austin, 2 December 1988
prof. dr. Edsger W. Dijkstra
Department of Computer Sciences
The University of Texas at Austin
Austin, TX 78712-1188
USA