DIJKSTRA 的 “GOTO 有害“:
最近几年我观察到,程序员素质与他们编写的program中go to语句的使用频率成反比。最近,我又发现了使用go to语句会产生灾难性后果的原因;并且越来越确信在“高级”程序设计语言中(除了原始机器语言的所有程序设计语言),应该禁止使用go to语句。以前,我对这个发现没有足够重视;而最近的几次关于这个问题的讨论,使我认为自己有必要向公众阐述我的想法。
更重要的是,这些索引的值是不受程序员控制的;无论他们是否愿意,索引都会被生成(不是被程序员的program所明确写出,就是被process动态生成)。他们提供了描述process进程的独立坐标系。
那么,为什么我们需要这样的独立坐标系呢?原因是——并且看上去更像是连续process的固有属性——我们只能通过process的过程来解释一个变量的值。如果我们需要计算一个房间中的人数,比如说n,初始值为0;那么我们必须在每一次看见有人进入房间时增加n。在中间的某个时候,如果我们看见一个人进入房间却没有增加n,那么n的值将比屋内实际的人数少1!
滥用go to语句会立刻使得在process过程中寻找有意义的、描述该process的坐标系变得异常困难。通常,人们也很重视那些经过精心选择的变量的值,但是毫无疑问,这是因为这些与该过程相关的变量是可以被理解的。使用go to语句,人们当然还可以从program一开始就唯一的计算一组动作来描述这个过程(即:一种规格化的时钟)。困难的是,这样的坐标系,尽管唯一,但是却完全没有用处。在这样一个坐标系中,定义过程中的这些点变得异常困难,比如说,证明n等于房间中人数减1!
go to语句看上去太原始了;并且很容易使得program变得混乱。我们可以乐观的考虑使用从句来抑制go to语句的使用。我并不认为上面提及的从句可以满足任何需求,但是无论如何,从句可以使程序设计者,以一种有益的并且是可操作的方式,建立独立的坐标系来描述process。
很难以一种公正的角度来结束本文。我是不是因为受某人的影响而变得太武断了?很明显,我受到了Peter Landin和Christopher Stracher的影响。最后,我想应该写下(对此我印象很深刻)Heinz Zemanek在1959年年初于Copenhagen召开的pre-ALGOL会议上的一段话。话中明确地表示:他怀疑,在任务语句和语法层面上,go to语句能否与其它语句一样被同等对待。谦虚地说,我应该责备自己没有重视他的论述。
不使用go to语句的论点不是什么新观点。我记得曾经读过一篇明确说明应该限制go to语句的建议性文章,但是现在无法找到它的出处了;推算起来,它可能是C. A. R. Hoare写的。[1, Sec. 3.2.1]中,在建议使用case construction方面,Wirth和Hoare一起得出了结论:“类似条件从句,case construction比起go to语句和switches更能清晰的描述program的动态结构,并且可以大量减少program中标签的使用。”
首先我想说的是,尽管程序设计者的工作在他构建完正确的program时就结束了,但是(不可否认)控制其program走向的process也是他工作中重要的一部分;这是因为正是在这个process中,期望的效果得以完成、动态的行为得以令人满意的按照计划实行。并且一旦program写好以后,就该由机器替代执行相应的process了。
其次我想说的是,人类的思维很适宜掌控静态联系,而形象化的能力却相对较弱。正是由于这个原因,我们应该(就像优秀的程序员了解自身的局限性一样)尽可能缩短静态program与动态process之间的概念差异;尽可能使program(在文本上展开)与process(在空间上展开)的相关性尽量密切。
现在来考虑如何描述一个process的过程。(您可以用一种很具体的方式来思考这个问题:假设process是一段时间内的一串连续动作,并且它在一个任意动作之后结束,那么使用什么样的数据能够使我们在同一点上重新执行这个process呢?)如果我们说,program文本是一串纯粹的任务语句(为了讨论,仅考虑它是单一动作),那么我们绝对可以找到一个点,这个点处在两个连续动作描述之间。(如果没有go to语句,我保证上个句子中的最后三个单词(连续动作描述)会有歧义:如果我们断句为“连续的动作描述”,其意思是连续的文本空间;如果断句为“连续动作的描述”,其意思时连续的时间。)我们不妨将这些在文本中恰当位置出现的点称作一个“文本索引textual index”。
当我们引入条件从句(if B then A)、二选一的选择从句(if B then A1 else A2)、由C. A. R. Hoare发明的多选一的选择从句(caseof (A1, A2, …, An)),或者由J. McCarthy发明的条件表达式(B1 → E1, B2 → E2, …, Bn → En)以后,process的过程中必存在一个文本索引。
在语言中引入procedure以后,我们必须承认一个单独的文本索引不再有效。由于文本索引指向procedure体内部,其动态过程只有当我们引用这个procedure时,才会被刻画。在procedure内部,我们可以通过文本索引序列刻画这个process的过程。序列的长度等价于procedure被动态调用的深度。
现在我们考虑循环从句(比如while B repeat A或者repeat A until B)。逻辑上讲,这样的从句是多余的,因为我们可以用递归的procedures来描述循环。但是由于现实如此,所以我也不想排除循环从句:一方面,循环从句可以通过现在的有穷设备很方便的实现;另一方面,被称作“归纳”的推理模式可以使我们的思维仅仅抓住由循环从句产生的过程。在循环从句的内部,文本索引对于描述process的动态过程不再有效。然而,在循环从句的每一次进入时,我们可以联合使用一种叫做“动态索引”的机制,客观地计算当前相应循环的循环次数。因为循环从句可以嵌套使用(就像procedure被调用一样),我们会发现,现在的process过程可以被文本或者动态的混合序列独一无二的描述。