### 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 控制结构的串联
除了串联,嵌套也是一种将多个语句组合成一个更大的程序块的形式。例如条件语句的
分支语句体和循环语句的循环体本身都是程序块。 结构化程序设计的原则就是利用“单入口单出口”的程序块进行串联、嵌套,最终搭建出复杂程序,这使得程序的结构清晰、层次分明、易理解、易维护。
除了上述几条设计原则,其他如模块化设计、自顶向下逐步求精设计也都是结构化程序 设计的基本内容,下一章对此有详细介绍。
- 前言
- 第 1 章 计算与计算思维
- 1.1 什么是计算?
- 1.1.1 计算机与计算
- 1.1.2 计算机语言
- 1.1.3 算法
- 1.1.4 实现
- 1.2 什么是计算思维?
- 1.2.1 计算思维的基本原则
- 1.2.2 计算思维的具体例子
- 1.2.3 日常生活中的计算思维
- 1.2.4 计算思维对其他学科的影响
- 1.3 初识 Python
- 1.3.1 Python 简介
- 1.3.2 第一个程序
- 1.3.3 程序的执行方式
- 1.3.4 Python 语言的基本成分
- 1.4 程序排错
- 1.5 练习
- 第 2 章 用数据表示现实世界
- 2.1 数据和数据类型
- 2.1.1 数据是对现实的抽象
- 2.1.1 常量与变量
- 2.1.2 数据类型
- 2.1.3 Python 的动态类型*
- 2.2 数值类型
- 2.2.1 整数类型 int
- 2.2.2 长整数类型 long
- 2.2.3 浮点数类型 float
- 2.2.4 数学库模块 math
- 2.2.5 复数类型 complex*
- 2.3 字符串类型 str
- 2.3.1 字符串类型的字面值形式
- 2.3.2 字符串类型的操作
- 2.3.3 字符的机内表示
- 2.3.4 字符串类型与其他类型的转换
- 2.3.5 字符串库 string
- 2.4 布尔类型 bool
- 2.4.1 关系运算
- 2.4.2 逻辑运算
- 2.4.3 布尔代数运算定律*
- 2.4.4 Python 中真假的表示与计算*
- 2.5 列表和元组类型
- 2.5.1 列表类型 list
- 2.5.2 元组类型 tuple
- 2.6 数据的输入和输出
- 2.6.1 数据的输入
- 2.6.2 数据的输出
- 2.6.3 格式化输出
- 2.7 编程案例:查找问题
- 2.8 练习
- 第 3 章 数据处理的流程控制
- 3.1 顺序控制结构
- 3.2 分支控制结构
- 3.2.1 单分支结构
- 3.2.2 两路分支结构
- 3.2.3 多路分支结构
- 3.3 异常处理
- 3.3.1 传统的错误检测方法
- 3.3.2 传统错误检测方法的缺点
- 3.3.3 异常处理机制
- 3.4 循环控制结构
- 3.4.1 for 循环
- 3.4.2 while 循环
- 3.4.3 循环的非正常中断
- 3.4.4 嵌套循环
- 3.5 结构化程序设计
- 3.5.1 程序开发过程
- 3.5.2 结构化程序设计的基本内容
- 3.6 编程案例:如何求 n 个数据的最大值?
- 3.6.1 几种解题策略
- 3.6.2 经验总结
- 3.7 Python 布尔表达式用作控制结构*
- 3.8 练习
- 第 4 章 模块化编程
- 4.1 模块化编程基本概念
- 4.1.1 模块化设计概述
- 4.1.2 模块化编程
- 4.1.3 编程语言对模块化编程的支持
- 4.2 Python 语言中的函数
- 4.2.1 用函数减少重复代码 首先看一个简单的用字符画一棵树的程序:
- 4.2.2 用函数改善程序结构
- 4.2.3 用函数增强程序的通用性
- 4.2.4 小结:函数的定义与调用
- 4.2.5 变量的作用域
- 4.2.6 函数的返回值
- 4.3 自顶向下设计
- 4.3.1 顶层设计
- 4.3.2 第二层设计
- 4.3.3 第三层设计
- 4.3.4 第四层设计
- 4.3.5 自底向上实现与单元测试
- 4.3.6 开发过程小结
- 4.4 Python 模块*
- 4.4.1 模块的创建和使用
- 4.4.2 Python 程序架构
- 4.4.3 标准库模块
- 4.4.4 模块的有条件执行
- 4.5 练习
- 第 5 章 图形编程
- 5.1 概述
- 5.1.1 计算可视化
- 5.1.2 图形是复杂数据
- 5.1.3 用对象表示复杂数据
- 5.2 Tkinter 图形编程
- 5.2.1 导入模块及创建根窗口
- 5.2.2 创建画布
- 5.2.3 在画布上绘图
- 5.2.4 图形的事件处理
- 5.3 编程案例
- 5.3.1 统计图表
- 5.3.2 计算机动画
- 5.4 软件的层次化设计:一个案例
- 5.4.1 层次化体系结构
- 5.4.2 案例:图形库 graphics
- 5.4.3 graphics 与面向对象
- 5.5 练习
- 第 6 章 大量数据的表示和处理
- 6.1 概述
- 6.2 有序的数据集合体
- 6.2.1 字符串
- 6.2.2 列表
- 6.2.3 元组
- 6.3 无序的数据集合体
- 6.3.1 集合
- 6.3.2 字典
- 6.4 文件
- 6.4.1 文件的基本概念
- 6.4.2 文件操作
- 6.4.3 编程案例:文本文件分析
- 6.4.4 缓冲
- 6.4.5 二进制文件与随机存取*
- 6.5 几种高级数据结构*
- 6.5.1 链表
- 6.5.2 堆栈
- 6.5.3 队列
- 6.6 练习
- 第 7 章 面向对象思想与编程
- 7.1 数据与操作:两种观点
- 7.1.1 面向过程观点
- 7.1.2 面向对象观点
- 7.1.3 类是类型概念的发展
- 7.2 面向对象编程
- 7.2.1 类的定义
- 7.2.2 对象的创建
- 7.2.3 对象方法的调用
- 7.2.4 编程实例:模拟炮弹飞行
- 7.2.5 类与模块化
- 7.2.6 对象的集合体
- 7.3 超类与子类*
- 7.3.1 继承
- 7.3.2 覆写
- 7.3.3 多态性
- 7.4 面向对象设计*
- 7.5 练习
- 第 8 章 图形用户界面
- 8.1 图形用户界面概述
- 8.1.1 程序的用户界面
- 8.1.2 图形界面的组成
- 8.1.3 事件驱动
- 8.2 GUI 编程
- 8.2.1 UI 编程概述
- 8.2.2 初识 Tkinter
- 8.2.3 常见 GUI 构件的用法
- 8.2.4 布局
- 8.2.5 对话框*
- 8.3 Tkinter 事件驱动编程
- 8.3.1 事件和事件对象
- 8.3.2 事件处理
- 8.4 模型-视图设计方法
- 8.4.1 将 GUI 应用程序封装成对象
- 8.4.2 模型与视图
- 8.4.3 编程案例:汇率换算器
- 8.5 练习
- 第 9 章 模拟与并发
- 9.1 模拟
- 9.1.1 计算机建模
- 9.1.2 随机问题的建模与模拟
- 9.1.3 编程案例:乒乓球比赛模拟
- 9.2 原型法
- 9.3 并行计算*
- 9.3.1 串行、并发与并行
- 9.3.2 进程与线程
- 9.3.3 多线程编程的应用
- 9.3.4 Python 多线程编程
- 9.3.5 小结
- 9.4 练习
- 第 10 章 算法设计和分析
- 10.1 枚举法
- 10.2 递归
- 10.3 分治法
- 10.4 贪心法
- 10.5 算法分析
- 10.5.1 算法复杂度
- 10.5.2 算法分析实例
- 10.6 不可计算的问题
- 10.7 练习
- 第 11 章 计算+X
- 11.1 计算数学
- 11.2 生物信息学
- 11.3 计算物理学
- 11.4 计算化学
- 11.5 计算经济学
- 11.6 练习
- 附录
- 1 Python 异常处理参考
- 2 Tkinter 画布方法
- 3 Tkinter 编程参考
- 3.1 构件属性值的设置
- 3.2 构件的标准属性
- 3.3 各种构件的属性
- 3.4 对话框
- 3.5 事件
- 参考文献