第四章 第五节 分析和解决问题的思维方式


正如前面学习的过程和过程的嵌套,这种思维方式能够把问题分解为许多易于管理的部分,同时还能帮助你梳理整个程序的脉络和逻辑,利于维护各个部分之间的关系。

当创建了许多过程之后,我们便能以特定的顺序使用它们解决编程问题。如果程序中多次使用了相同的过程,就可以避免代码的复制和粘贴。(即代码复用)。

用过程的思维解决复杂问题的本质就是“分而治之”:将一个很大很复杂的问题分解成许多小的子问题,然后分别解决并独立地测试每一个子问题,最后将这些子问题整合在一起,从而解决了最初的问题。

现在我们就来讨论一下如何分析问题。不同问题之间虽然都存在差异,也没有千篇一律的解决方法,但这正是解决难题的魅力所在!

下面我们来学习两种分析复杂问题的方式:自顶向下分析和自底向上分析。前者将大程序模块化,使其拥有清晰的逻辑结构;后者从最简单的问题入手,逐步构建完整的程序。

无论哪种方式,待解决的大问题都是上面的大圆圈,而下面的小圆圈则是构成大问题的众多小问题。具体使用哪种方式取决于问题的场景。

自顶向下分析
充分理解问题是解决任何编程问题的第一步。理解之后,我们可以做出一个大致的解决方案,然后将其划分为多个主要任务。程序分解的结果是因人而异的,一般没有正确和错误之分,但通常需要明确“主要任务”的含义,而且至少要保证程序的整体逻辑是正确的。
我们先来看一个绘制房屋的案例,绘制结果如下图4-24所示。该案例分析问题的方式便是自顶向下的。

这个绘图计划看似简单,但是它说明了什么叫作自顶向下的分析,避免了我们一开始就陷入绘制房屋的细节中,而且它还启发了我们转换主要任务,例如:
(1)认为房屋是由直线构成的。这种情况下,主要任务就是绘制每一条直线。
(2)将房屋视为六个独立的形状:侧面一侧面二、两扇门、一个三角形和一个平行四边形。此时主要任务是绘制每一个形状。
(3)由于两扇门的形状是一样的,因此可以定义主要任务为绘制门,然后重复调用两次。
(4)将屋顶的三角形和平行四边形视为一个整体。这时主要任务是绘制屋顶。
(5)将侧面一和侧面一的门视为一个整体,即前门。这种情况下,主要任务是绘制前门。
当然还有更多的可能,这足以说明主要任务的定义是因人而异的。总的来说,自顶向下分析就是把某个任务划分为更小、更易管理的片段。你只需要依次关注各个片段并逐一解决。如果你发现某些片段有相似之处,尝试归纳并使用通用的解决方法。

图4-24的房屋绘制计划假设角色位于点A面向右。我们只需要创建与绘制计划相匹配的过程即可完成绘制。根据步骤一,我们首先创建过程Side1(侧面一);根据步骤二、三、四和六,我们继续创建过程Door(门)、side2(侧面二)和Roof(屋顶,包含一个等边三角形和平行四边形),从而绘制两扇门、房屋的右侧以及屋顶。最后将所有过程用动作和画笔模块连接起来。

主程序如下:设置一个绘制的长度单位变量scale,然后分别调用各个过程实现全屋的绘制。各个过程由大家自行动手完成吧!

学习几个英文单词:

1.Roof(屋顶)2.triangle(三角形)3.parallelogram(平行四边形)4.rectangle(矩形,长方形)

rectangle

进一步观察与思考,我们会发现侧面一、侧面二和两个门都是矩形,那么我们是否可以创建一个绘制矩形的过程(其参数有长、宽及长度单位scale)?通过调用同一个过程,实现绘制侧面一、侧面二和两个门,进一步简化程序。

自底向上分析

集中精力解决复杂问题的某个细节是另一种分析问题的方式。如果可以解决复杂问题的各个细节,我们就能把它们组装起来,从而自底向上地解决问题。

下面通过一个简单的程序看看这种思维方式。程序运行的结果是绘制如下图左的花朵。通过观察,花朵由枝旋转6次而成,而枝是由叶和花组成,花又是由叶旋转5次而成。最终的问题变成了只要绘制出叶,就能绘出花朵来。

leaf 英[li?f] 叶子

leaves 英[li?vz]  叶子(复数)

branch 英[brɑ?nt?]  树枝

flower 花朵



通过这个案例,你明白什么叫作自底向上的分析了吗?我们一开始并不关注整个问题的复杂性,而是先从更小、更容易管理的片段着手,然后将这些片段整合在一起,从而解决了整个问题。使用这种思维方式,我们可以先创建过程解决简单的问题,再创建更复杂的过程调用之前的过程。