计算机先驱——莫里斯·威尔克斯

Sir Maurice Vincent Wilkes,1913年6月23日-2010年11月29日
1931年他进入剑桥的圣约翰学院,之后进入剑桥著名的卡文迪什实验室工作。于1938年10月取得剑桥大学博士学位,而他的硕士学位是在当年年初才取得的。
英国计算机科学家。设计和制造了世界上第一台存储程序式电子计算机EDSAC,在“工程和软件等计算机领域都有许多开创性成果”。

temp

开端

这是1946年的一个温暖和煦的仲夏日,时值星期一早晨。回想起三年前与图灵在下午茶时间的热烈讨论,克劳德·香农觉得恍如隔世。此时此刻,他正在宾夕法尼亚大学的摩尔电气工程学院学习一门为期八周的课程,这门课目前已经上到了第三周。能够受邀来此听课,对看他来说已是一大荣幸,毕竟,这是少数人才有的殊荣。课程采用讲座的形式,主题是电子数字计算机的设计。这是世界上第一门关于计算机科学的课程,香农发现,课上所学的许多思想在他的头脑里擦出了灵感的火花。他最近从莫奇利那里学到了一个新词——“编程”。它通常作为动词使用。给电子计算机编程是一个让人耳目一新的概念。香农还听到了一些关于办公室政治的八卦:给他们开课的两位讲师——莫奇利及其同事埃克特四个月前刚从宾夕法尼亚大学辞职,原因似乎是围绕“埃德瓦克”产生的专利纠纷。另一名讲师戈德斯坦近期就要去高等研究院(IAS)入职了,届时他将与香农的老同事约翰·冯·诺依曼共事。冯·诺依曼本来也要到这里来开设两个星期的讲座,但是他好像不会来了,因为临时有事。

在一篇机密级的论文中,香农发布了他最近在贝尔实验室开展密码学研究的部分成果。这些研究成果与他当年攻读理科硕士期间提出的思想一脉相承,只不过二者的着眼点不同。香农当年着眼的是开关电路现在着眼的则是密码学背后的数学原理他开始意识到,破译扰频加密的信息,其实大体上就相当于给正常传输的信息纠错。举个例子,如果一段编码中出现了重复信息——比方说它的内容中包含许多常见的词语如“这个”、“一个”、“和”,那么这段编码破译起来就会轻松许多。因为我们知道,“这个”、“一个”、“和”这类简短的词语是句中常见的语法成分,就算把它们的每个字都改头换面,也不难根据它们在句中出现的位置来判断其成分。只要破译了高频词,那么整条加密信息指的是什么内容,或许就能猜出个大概了。因此,要想增加破译难度,必须尽量减少冗余信息。但是,如果你想尽可能多地保留原始信息,而记忆存储系统又很容易出错,那就最好少删除一些冗余信息。
检验信息是否出错的方法之一,就是设置奇偶校验位(parity)。至于奇偶校验位是指什么,还得从比特的概念说起。比特(bit)是内存中的最小单位,也称作“位”、它只有两个状态,分别以1和0表示。我们将8个连续的比特叫做一个字节(byte),比如(1、0、0、1、1、1、1、0)就是一个典型的字节。如果其中某一位存储了错误的值,那就会导致信息出错。为了检测信息是否出错,我们在每一个字节(8位)后面又额外增加了一位,称为奇偶校验位。这样一来,原来的8位字节就变成了新的9位字节。奇偶校验位也只有1和0两种值。如果原字节中1的个数为奇数,那么奇偶校验位就设为1,这样一来,新字节中1的个数就变为偶数;反之,如果原字节中1的个数为偶数,那么奇偶校验位就设为0,这样一来,新字节中1的个数依然为偶数。也就是说,凡是带有奇偶校验位的字节当中,1的个数始终应该为偶数,如果你发现某个字节不是这样,那就说明它有错误,这段字节包含的信息就需要重新读取。(这个方法在20世纪50年代早期开始在计算机领域广泛采用,后来,人们很快就开发出了更多高级的方法。)

言归正传,香农在摩尔学院听讲座的过程中,了解了二进制对于计算机的重要价值:二进制数不仅是计算机内各个部件交换信息的重要载体,还是确保信息存储和检索过程不出差错的重要工具。后来,香农发明了术语“比特”来指代二进制数位(binary digit),同时阐述了如何利用比特来衡量信息量,并对信息进行传输、加密、压缩、纠错。
在当年赴摩尔学院听讲座的人当中,香农并不是唯一一个计算机领域的先驱。莫里斯·威尔克斯也去了摩尔学院,只不过他差点就错过了这门课程。香农在台下听莫奇利讲解二进制与十进制数的那一天,威尔克斯还在英国。
EDSAC

1945年,莫里斯·威尔克斯在剑桥大学听说了埃克特和莫奇利在美国研制埃尼阿克的工作,于是在1946年2月给学院提交了一份报告。他在报告中写道:“这是一个大有可为的研究领域,电子应用技术首当其冲。它在战时取得了迅猛的发展。 美国人在这门学科上已经领先了一步,我觉得剑桥也应该迎头赶上。”
三个月后,机械计算机专家莱斯利·科姆里(Leslie Comri)来访剑桥。他从美国带来了一份手稿副本——即约翰·冯·诺依曼所写的《关于埃德瓦克的报告初稿》。威尔克斯只有一个晚上的时间阅读报告,当时还没有影印机,他只能边读边作笔记。威尔克斯很快就被报告的内容吸引住了。“我很快就意识到这个研究成果非同小可,”他表示,“从那以后,我对计算机的发展前景一直没有怀疑过。”就在威尔克斯依然对报告的内容记忆犹新之时,他突然接到了摩尔学院院长哈罗德·彭德(Harold Pender)发来的电报,邀请他去参加一门新开的电子计算机课程。
威尔克斯的越洋航程并不顺利。尽管伙食条件一流,但是住宿条件太差,35个人挤在一艘只能容纳20个人的小货船里。更糟糕的是,发动机在中途抛锚了好几次。就这样,威尔克斯一路历经磨难,终于在8月15日抵达了纽约,上岸后又马不停蹄地赶路,总算在8月18日赶到了费城。这时候,他已经错过了三分之二的课程,好在前面的课程大多都是些入门性质的讲座。到了8月19日,也就是星期一,威尔克斯抵达摩尔学院,正好赶上当天下半节课。这堂课讲的是埃尼阿克的细节内容,讲师提供了详尽的电路图。
edsac1

威尔克斯回到剑桥大学,满脑子都是计算机领域的前沿思想和美国人取得的一些关键成果。
威尔克斯认为,是时候研制一台实用的存储程序计算机了。幸运的是,没过多久,餐饮巨头J. Lyons & Company就给他提供了科研经费和技术人员,因为该公司需要计算机进行会计核算,管理员工工资单。威尔克斯的项目进展很快,他在雷达领域积累的经验更是大大加速了这一进程。项目团队在此基础上构建了一个工作记忆系统,用于存储数字。
edsac2

威尔克斯在研制新机器的过程中,旁听了图灵开设的几个讲座,讲座的主题是图灵关于自动计算机的设计思想。不过,两个人的设计理念并不一致。图灵认为,计算机应该在水银延迟线存储器的基础上进行优化设计。威尔克斯的观点正好相反。“我认为,水银延迟线存储器迟早要被真正的随机存取存储器淘汰。与其把时间和精力都花在一项短命的技术上,还不如多下点功夫研究编程,毕竟,编程领域还有那么多问题值得研究。”图灵也不赞赏威尔克斯的设计理念,他在一篇备忘录中写道,威尔克斯有一些理念“比较偏向美国传统,遇到什么困难就喜欢依赖设备,而不喜欢动脑子。”
但是事实证明,威尔克斯的方法更加实际。剑桥大学的电子延迟存储自动计算机(Electronic Delay Storage Automatic Calculator,简称EDSAC)于1949年5月6日投入运行,直到1958年才光荣退役。它是世界上第一台实用的存储程序计算机。
edsac3

学习计算机编程

剑桥大学的EDSAC计算机并不只是一台前沿尖端的机器,它还开创了计算机领域的先河。此前的计算机每次执行新的运算,都需要插入不同的线路进行重新装配,而EDSAC则通过存储器中的软件实现各种不同的运算操作,这就对编程提出了很高的要求。为了写出功能强大的软件,威尔克斯和戴维·惠勒(David Wheeler)等研究人员提出并改进了许多新的思想,时至今日,这些思想在计算机编程领域已占据主流地位。
不过编程在当时并不是一件容易的事情。所有的早期计算机先驱很快就意识到,一旦设计出存储程序计算机,就必须拼了老命地编写计算机能够运行的程序。如果任何程序都无外乎是一组能够触发数学或逻辑学电路的二进制数,那么编写软件就会变成一场噩梦——而事实也的确如此。1949年6月,惠勒第一次意识到了编程的艰难。他后来回忆起了当时的情形:“那时候,我正试着让自己编写的第一个真正意义上的程序运转起来。有一次,我像往常一样从EDSAC机房出来,准备去操作打孔机,突然站在楼梯转角处犹豫了,心里意识到,单是给自己的程序除错,可能就要花掉我大半辈子的时间。”
显然,当时的科学家需要一些新的思想,来简化编程过程,提高编程能力。对此,威尔克斯(在几年后)提出了一个方法,称为微程序设计。当时,麻省理工学院正在研制的旋风计算机(Whirlwind)给了他部分灵感,让他意识到,并不是每一条低级指令——比如除法——都需要电子电路来执行。复杂的指令完全可以分解成一系列简单的指令,而微代码编写出来的微程序可以作为二进制机器代码和硬件之间的桥梁。事实证明,这一方法实用性很强,时至今日,复杂指令集计算机(Complex Instruction Set Computer,简称CISC)处理器依然采用了这一思想原理,以执行高度复杂的操作。而精简指令集计算机(Reduced instruction set computer,简称RISC)处理器则不使用微编程。
但是即便是使用微代码,计算机编程人员依然需要编写一长串的数字,即机器代码指令。而且,早期的编程人员还因为机器的内存容量极其有限而备受掣肘。EDSAC的内存只有两千字节左右(放到今天,一部手机的内存都比它大几百万倍)。为了解决这些问题,威尔克斯的团队又想出了一个妙招——编写子程序。研究人员意识到,许多程序在运行的过程中,都需要重复执行某个操作——比如在某个复杂的数字运算中,需要多次进行开平方操作。如果每次开平方都得把平方根代码写上,那么程序当中就会出现许多重复代码,占用不必要的空间,使程序变得庞大而低效。这就好比你在写一个句子时,不仅构造了完整的语法结构,还将句中每个词语的定义也写了下来。
为了简化编程过程,威尔克斯的方法是建立子程序库,也就是将常见的函数单独列出,集中起来,就像把常见的词语及其释义收录在词典中一样。一旦程序在运行的过程中需要使用到某个常见函数,计算机就会在子程序库中“查找定义”,执行相应的子程序代码,根据输入值进行运算,再将运算结果返回。
在这一方面,威尔克斯的理念已经领先于同时代的大多数人。冯· 诺依曼有一次突然造访,与威尔克斯进行了讨论。两个人的观点产生了分歧。威尔克斯回忆道:“他觉得应该把开平方运算嵌入到计算机的指令集中……我自己的立场则有所不同,我已经把子程序看做是对基本指令集的扩展,所以觉得没有必要再在指令集中嵌入一个特殊的函数。”
由于威尔克斯领导的剑桥大学研究团队很早就开始在编程领域开疆拓土,他们对编程的艺术也颇有心得。普林斯顿高等研究院的冯·诺依曼喜欢绘制“程序框图”,来展示程序应该如何运作。所谓程序框图,大体上就是一系列带箭头的步骤,可以展示控制流。不过,威尔克斯手下最好的程序开发人员戴维·惠勒认为,要想写出出色的软件,还需要其他的方法。“我们关心的首要问题是,用户在操作过程中是否得心应手,因此在这方面也下了很大的功夫。”他表示,“模块化设计的编程风格很早就开始得到推行。这是我们开展编程教学的方式。当然,我们也知道一些其他的方法,比如冯·诺依曼的程序框图。不过这些方法一般都不用,就算用也只是拿来说明已经完成的步骤。我 们发现,只要将复杂的问题进行分解,用一个个子程序加以解决,然后将子程序置于主程序的控制之下,就可以逐步形成模块化设计的思维方式,到时候程序设计自然水到渠成,根本不需要使用到程序框图。个人认为,程序框图最容易让人写出垃圾软件。并不是说这种方法毫无价值,只不过你要是想拿它来代替思考过程,根本就不管用。”
威尔克斯的团队还考虑到了程序员可能遇到的其他困难。他们从一开始就意识到,尽管计算机只能理解数字,但很少有人能够在只采用数字的情况下编写程序,了解计算机的运行过程。人的大脑习惯了阅读文字和符号,而不是处理一连串数字。惠勒认为,冯·诺依曼在高等研究院研制的计算机在这一方面做得很差,完全比不上剑桥大学的 EDSAC。“它太原始了,这让我非常震惊,”他表示,“我估计它的程序是用二进制输入的。我们剑桥大学的研究团队很早就开始采用了一种叫做‘汇编程序’的工具。”它能够转换编程语言,从十进制转为二进制,使用助记符,可引用代码,可分隔字段,可自动定位子程序,还具备其他各种功能。我们基本上从开展项目的第一天起就已经在使用汇编程序了。这极大地简化了编程过程。
这样一来,程序开发人员就不需要和抽象的二进制数打交道了,他们可以采用简短的词语来编写程序,这些词语看起来有点像英文单词。
与一连串抽象的二进制数字相比,即使是奇怪而又晦涩的文字无论在阅读还是理解上,都要轻松许多。它与处理器使用的低级的代码没有太大的分别:其中的每一个词语(或命令)——比如“cmpl”、“jmp”——都直接对应于机器代码中的一条指令。要将汇编语言写成的程序转换为相应的机器代码,就需要使用到另一种计算机程序,称为汇编程序。汇编程序读取的是用汇编语言书写的源程序,输出的是用机器语言表示的目标程序。
汇编语言在计算机领域举足轻重,20世纪90年代的所有程序都是由汇编语言写成。几十年来,有两种常见的程序只采用汇编语言:一是计算机游戏(因为开发人员希望尽可能地提高游戏的运转速度,同时尽可能给玩家带来极致丰富的游戏体验),二是操作系统。即便是在当今时代,程序员要想编写速度超快而形式简洁的代码,都免不了要采用一些汇编语言。

攀登更高峰

1951年,计算机开始搭载好几个层次的软件。第一层是微代码,它完全依赖于芯片内部的硬件连接。第二层是机器代码,它比微代码更抽象一些。第三层是汇编语言,它比机器代码可读性稍强。
计算机编程,说白了其实就是告诉计算机应该使用哪种逻辑和算术电路。要想给计算机编程,程序员可以使用汇编语言来写代码,而汇编语言正如我们在上一节所看到的那样,和英文单词有些类似。这些代码随即由汇编程序转化为机器代码机器代码定义微程序的指令微程序则在算术和逻辑单元(ALU)的电子元件中被翻译为一系列指令的组合。 不过,汇编语言对于许多程序员来说依然难度太大。如果你想处理更复杂的思想和概念,那么,纠结于个别的跳转指令只会拖累你的步伐。 如果你希望自己的程序能够在完全不同的处理器上运行,那就需要采用高级的编程语言,也就是独立于底层硬件的计算机语言。
1953年,随着电子计算机在全世界遍地开花,发明抽象编程语言的问题开始受到广泛关注。莫里斯·威尔克斯受邀主持了美国计算机协会(ACM)在麻省理工学院召开的一期研讨会,专门探讨这个问题(当时的会议主题为自动编程)。威尔克斯至今还清楚地记得当年会上的讨论情况。“与会者的意见分歧相当尖锐。有些人认为,凡是试图绕开困难的做法都是误入歧途。程序员只有老老实实地恪守本分,编程领域才会取得更大的发展。另一方面,还有一些人认为,只有新的技术才具有实实在在的实用价值。”(时至今日,计算机科学家当中依然存在这两派的纷争。)
许多研究人员已经在试验新的、更简单的编程语言。早在1949年,约翰·莫奇利就发明了一种语言,称为简代码(Brief Code),后来更名为短代码(Short Code)。短代码虽然使用起来简单许多,但是需要翻译——也就是说,计算机每次运行这种语言编写的程序,都得临时将短代码翻译成机器代码。这就意味着,这种程序的运行速度比机器代码写成的程序慢54倍。与此同时,在英国曼彻斯特,一位名叫埃里克·格伦尼(Alick Glennie)的研究人员发明了另一种语言。
这种语言易于使用,可通过另一种程序自动转换为机器代码,因此具备简单实用、运行速度快的双重优势。格伦尼将其称为自动代码(Autocode)。 显然,用自动代码写程序比用汇编语言要轻松许多。其中有些自动代码语句和好几条机器代码指令相对应,不过程序员不需要为此挂心。只要使用另一种称为编译器的程序,就可以将这段由自动代码编写而成的简洁英文命令翻译成机器代码自动代码是世界上最早出现的编译型高级编程语言。
自计算机协会在麻省理工学院召开研讨会,讨论自动编程问题后,越来越多的计算机科学家开始意识到,编程过程的简化势在必行。1957年,IBM的约翰·巴库斯(John Backus)发明了另一种编译型高级编程语言,称为福传(FORTRAN)。福传的全名是Formula Translation,意思是“公式翻译”。这种语言甚至比自动代码更高级,可以用来编写更加复杂的程序。它的编译器也极为智能,可以生成非常简洁高效的机器代码。
没过多久,许多适用于其他计算机的福传编译器相继问世,这样一来,同一款福传程序就可以编译成不同计算机所特有的机器代码。从这一刻开始,程序员便有了一种新的工具——“便携式”代码,它可以使同一款程序在完全不同的计算机上运行。很快,其他编程语言也开始纷纷效法,比如算法语言(ALGOrithmic language,简称ALGOL)、列表处理语言(LISt Processor,简称LISP)、初学者通用符号指令码(Beginner’s All-purpose Symbolic Instruction Code,简称BASIC)。随着计算机设计师开始研制晶体管计算机,这些语言也得到了稳步改进。
很快,计算机科学家便能分析编程语言,并用数学方法——包括邱奇的λ演算将其形式化。程序员也可以在代码中表达更抽象的概念,而不需要操心低层次的细节问题。各种类型的编程语言相继问世。早期语言基本上都是过程式语言(程序员告诉计算机如何执行过程步骤)后来的语言则采用了不同的编程范型在面向对象的语言(Object-Oriented Language)中,数据及其操控方法都封装在“对象”中,以实现代码的模块化,防止数据的意外损坏(这一点也是程序的“副作用”)。函数式编程语言利用若干简单的执行单元让计算结果不断渐进,逐层推导复杂的运算,而不是像过程语言一样,设计一个复杂的执行过程。此外,还有更多适用于并行计算机的程序语言相继问世。
正因为众多早期先驱的开创性工作,如今人们习以为常的一些重要编程思想才得以诞生。威尔克斯继续投入计算机语言的研究,他在改良算法语言60(ALGOL 60)的基础上发明了CPL(Combined Programming Language)编程语言。这种语言并没有受到热烈的反响,但是却奠定了BCPL(Basic Combined Programming Language)语言的基础。BCPL进一步发展演变,推动了B语言和C语言的问世。直到现在, C语言(以及在此基础上形成的诸多语言,如C++、C#、Objective C)或许是世界上应用最广泛的计算机编程语言之一。人们当前使用的许多众所周知的操作系统(比如UNIX、Linux、Mac OS X、Windows)都是用C语言写成的。如今,几乎每一台计算机上都搭载了C语言编译器,方便用户使用C语言编写代码。