3.5.2 结构化程序设计的基本内容
简单问题的求解过程通常是直接了当的,可选择的执行路径不多;但对于复杂问题,一般能设计出多种求解过程。在各种求解过程中,有些过程会比其他过程“好”,当然这个“好” 的意义是依赖于具体问题的。打个比方,为了烧一壶开水,恐怕所有人都会按照“向壶中加 入冷水;壶放到炉子上;点火烧至沸腾”这样的过程来解决。但如果是烧冬瓜排骨汤,则外 行会将冬瓜和排骨一起入锅加水煮;有点经验的人则知道先煮排骨,排骨快熟了才加冬瓜一 起煮;而老练的厨师则会先将排骨焯水,然后再加水煮,排骨熟了才加冬瓜。如果再考虑各 种佐料的使用,显然冬瓜排骨汤的制作过程是多种多样的。哪种制作过程好呢?美食家会告 诉我们厨师的做法是好的,因为按他们的做法能保证排骨熟透而冬瓜不烂,而且焯过水的排 骨更干净并可减少油腻。
至此,一个问题摆在了我们面前:如何设计出能解决特定问题的“好”的程序?为了回 答这个问题,需要先定义什么是“好”程序。一般来说,好的程序不但要能正确地解决问题, 而且还应该是执行效率高、易理解、易维护、可扩展的。
程序设计过去曾被看作是个人技艺,程序的好坏完全依赖于程序员的个人才能。但后来 计算机科学家们认识到,程序设计是一门可以给予科学解释的学问,可以建立良好的设计方 法来指导程序员进行程序设计。普通程序员只要遵循这些设计方法,都能编写出良好的程序。
计算机科学家提出了许多程序设计方法,最早提出也是最基本的一种方法就是这里要介 绍的是结构化程序设计(structured programming,简称 SP)。SP 是以 Dijkstra 为代表的计算 机科学家于上世纪 60 年代后期建立起来的,是从程序文本结构的角度来阐述怎样的程序是良 好的。SP 的基本思想是要确保程序具有良好的结构,使程序易理解、易验证和易维护。当然, SP 并没有一个严格的、公认的定义,其具体内容大致包括以下几个原则。
只用三种基本控制结构 解决复杂问题时,程序可能需要建立复杂的控制流程,这是不是意味着编程语言应该提供更多的复杂控制结构呢?答案是否定的。计算机科学家证明了所谓“结构化定理”:任何程 序逻辑都可以只用顺序、条件分支、循环这三种基本控制结构来实现。因此,我们在开发程 序时,应该只使用这些基本控制结构,并将它们串联、嵌套在一起,从而搭建出整个程序。 本章前面介绍了条件分支和循环控制结构的多种常见使用模式,读者应当熟练地掌握这些模 式。当遇到复杂问题时,可以利用流程图工具,将复杂的控制流程转化成这些基本控制结构 的串联和嵌套。
goto 语句是有害的
较老的编程语言(如 BASIC、Pascal 和 C 等)中提供了 goto 语句,这条语句的作用是将 控制直接转到程序中的指定位置。使用 goto 可能写出这样的代码:
……
ENTRY: count := 0; while count < n do
begin
……
if sthWrong then goto EXIT else goto ENTRY;
end;
EXIT: writeln("End");
……
goto 语句看上去用起来很直接、很方便,很多人在设计程序流程遇到麻烦时第一感就会 想用 goto 语句。但是可以想象,如果程序中大量使用 goto 来控制程序的流程,这样的程序 就像一团乱麻,程序的静态结构与动态执行不一致,是非常难理解、难维护的。Dijkstra 首先 提出 goto 语句是有害的,并提出应当编写结构清晰的程序,以使程序易写、易读、易验证和 易维护。
事实上,goto 语句并非必须的语言构造。计算机科学家证明了,使用 goto 的程序一定可 以转化为只包含顺序、条件分支和循环结构的程序,也就是说编程语言中完全可以将 goto 语 句去除。
与 goto 类似的语句还有循环中使用的 break 和 continue 语句,它们都是以跳转的方式将 控制转移到程序其他位置,导致循环有多个出口。按照 SP 的思想,这些语句都应慎用。
单入口单出口的程序块
编程语言的单条语句可以看成是只有一个入口和一个出口,因此前后相继的语句序列构成了单入口单出口的顺序控制结构。而条件控制结构(if 语句)和循环控制结构(for 和 while 语句)的内部虽然可以出现由多条语句构成的语句块,但从外部看同样是只有一个入口和一 个出口(参见图 3.1、图 3.4 等流程图)。总之,基本控制结构(顺序、条件、循环)都是单入口单出口的结构,这种结构具有“可组合”的特性。 如果将两个基本控制结构串联在一起,前一个结构的出口连接后一个结构的入口,那么
所得到的语句序列仍然只有一个入口和一个出口,在效果上完全可以视之为单条语句(见图 3.11)。这就像电子电路中将两个电阻串联后可以视为一个更大的电阻一样。不断重复这个串 联过程,将得到由多个控制结构串联而成的结构,它仍然只有一个入口和一个出口,我们称 之为程序块。由于程序块只有一个入口和一个出口,在不考虑其内部控制结构的情况下,完 全可以将整个程序块视为单条语句,从而可以在不改变其内部控制流的情况下用于程序中任 何可以出现语句的地方。
图 3.11 控制结构的串联
除了串联,嵌套也是一种将多个语句组合成一个更大的程序块的形式。例如条件语句的
分支语句体和循环语句的循环体本身都是程序块。 结构化程序设计的原则就是利用“单入口单出口”的程序块进行串联、嵌套,最终搭建出复杂程序,这使得程序的结构清晰、层次分明、易理解、易维护。
除了上述几条设计原则,其他如模块化设计、自顶向下逐步求精设计也都是结构化程序 设计的基本内容,下一章对此有详细介绍。