引言
通过“妙用篇”的学习,我们发现,Scratch能做的事情确实很多,能够实现我们大脑中涌现的各种奇思妙想。而在实现这些奇思妙想时,我们的Scratch编程水平也在不知不觉间得到了提升。你可能对编程有了更大的兴趣,想去探索那些难度更高、也更有挑战性的领域,而不仅仅局限于实现一些动画、游戏之类的作品,你可能会想,Scratch能不能帮我解决一些更复杂的问题呢?
是的,编程的目的,本身就是为了帮助人们解决一些单纯人力解决起来麻烦或者比较困难的问题。Scratch作为一种入门型编程语言,虽然用于生产生活中还有些不足,但是,这不妨碍我们利用它做一些有益的尝试。比如:解决一些数学领域的趣味问题、研究神秘的分形图、进行逻辑推理、实现更快速有效的排序、查找等。这一切的背后,都需要算法。
算法是程序的灵魂,我们在以前编写程序的时候,你拖进编程区域的每一块积木都是在为实现你大脑中的算法服务的。算法实现了,你的程序功能就实现了。因此,不管你有没有意识到,你和算法已经打了很长一段时间的交道了。但是为了让我们的程序更有效、更精练、更能应对现实问题的挑战,我们需要在算法领域做进一步的训练,去学习前人已经总结的许多精彩算法,将其消化吸收,成为自己解决问题的智慧。这就是我们“进阶篇”要实现的目标。
在接下来的学习中,我们会遵循问题——分析——编程——总结的模式,每一节都会提出一个问题,分析用Scratch怎样解决它(设计算法),编写程序,最后进行总结。由于已经是进阶阶段的学习,我们就不会再去讲解具体指令的用法,我们相信,即使有些指令你没有用过,你完全可以通过上手试验的方式迅速掌握。
下面就开始第一个问题的讲解。
问题
森林里举行了一场运动会,小狗、小兔、小猫、小猴和小马参加了百米赛跑。比赛结束后,小动物们说了下面的一些话:
小猴说:“我比小猫跑得快。”
小狗说:“小马在我的前面问过了终点线。”
小兔说:“我的名次排在小猴的前面,小狗的后面。”
请你编写程序,根据小动物们的回答排出他们的名次。
分析
这是一个逻辑推理类的问题。什么是逻辑推理呢?它是指根据周围环境和活动找出其内在的逻辑关系,从而推理出符合其内在逻辑关系的结论。只有具备了逻辑推理能力,才能对事物做出符合逻辑关系的正确判断,因此逻辑推理能力也是基本个人素质之一。比如无所不知的名侦探柯南,总能根据环境和活动中存在的蛛丝马迹,找到“永远只有一个”的“真相”,这个“真相”,就是我们用逻辑推理得到的“结论”。
要解决逻辑推理问题,我们需要根据已知的条件,提炼出正确的逻辑关系,再将这些关系用Scratch中的逻辑表达式来表示。那么什么是逻辑表达式呢?用逻辑运算符(与、或、不成立)将关系表达式(大于、小于、等于有关的式子)连接起来的有意义的式子就是逻辑表达式。比如:
这张图中有三个逻辑表达式,分别是2>1,2<3和1=2。我们逐个来看看:
- 2>1,很明显这是“对”的,也就是它“成立”,所以当你点击这个积木,Scratch会告诉你它的值是“True”,也就是“真”或“成立”的意思;
- 2<3,同样,它也是成立的,所以表达式的值也是“True”;
- 1=2,这就有问题了,只要学过数学的人都知道这不对,1怎么可能等于2呢?这是两个不同的数字嘛,所以1=2不成立,这个表达式的结果就是“假”的,在Scratch中会把它表示成“False”,是“假”对应的英文单词。
也就是说,关系运算得到的值要么是“真(True)”,要么是“假(False)”。这种值称为逻辑值或布尔值,它还可以用“与、或、不成立”这三个逻辑运算符连接起来,组成成更复杂的逻辑表达式:
有的同学可能想起来了,这不是做“剪刀石头布”判断胜负的吗?根据计算机的选择和你的选择:
- 如果你选择了剪刀 而且 计算机选择了布
- 如果你选择了石头 而且 计算机选择了剪刀
- 如果你选择了布 而且 计算机选择了石头
很明显,上面三个条件都是用“与”连接起来的关系表达式,每个表达式中你和计算机的选择都符合设定,那么 你就胜利;而这三个条件的情况只要出现一种,都代表你胜利,所以我们会用“或”运算符把它们连接起来。
是不是有点绕呢?其实这种逻辑表达式我们已经用得很多了,只要你使用“如果……那么……否则”积木,你在“如果”后面放的条件就是一个逻辑表达式,它的值为“真”,就会执行“如果”后的代码,它的值为“假”,就会
执行“否则”里面的代码。同样,“重复执行直到……”这种积木也是重复执行到你设定的条件为“真”时才结束,“等待……”积木是你设定的条件为“真”时,才执行它后面的代码。
有没有发现它们的共同点就是那个六边形的空白?这就是提醒你,要放逻辑表达式在里面。逻辑表达式可以是关系运算的结果,也可以是Scratch帮我们做的判断,比如碰到颜色、碰到鼠标指针、字符串是否包含另一个字符串等等,你会发现它们的确都是六边形的,与上面要求放条件的位置正好匹配。
好了,复习完毕,让我们回到问题。
题目中给出的三个条件,乍一看,我们会觉得不容易关联到一起。但如果我们把每个小动物的排名用数字变量表示,那么就可以把三个条件转换为逻辑表达式了:
- 小猴说:“我比小猫跑得快。” 这说明小猴的名次靠前,如果用数字表示,名次越靠前,数字越小,所以小猴< 小猫;
- 小狗说:“小马在我的前面问过了终点线。” ——这意思就是小马名次在小狗前面嘛,所以小马 < 小狗;
- 小兔说:“我的名次排在小猴的前面,小狗的后面。”——这个要稍微复杂一点,说明小兔的名次在小猴前面、小狗后面是同时成立的,是“与”的关系,如果用数字表示名次,那么可以写出逻辑表达式 (小兔 < 小猴)与(小狗 < 小兔)
于是,我们从三个条件得到了三个逻辑表达式:
- 小猴< 小猫
- 小马 < 小狗
- 小兔 < 小猴)与(小狗 < 小兔)
有了这三个表达式有什么用呢?还是不知道每个小动物的名次啊?是的,目前我们只是把名次之间的关系梳理了一下,并不知道具体的名次。但是仔细一想,总共就五只动物,那么每只动物的名次无非就是1-5之间的一个数字嘛?我们可以重复把每种动物1-5的名次组合起来算一遍,算的过程中只要发现一种组合是同时满足这三个条件的,是不是这个时候就找到了每个动物的名次了呢?
对了,这就要用到我们以前讲过的“枚举法”了,我们把每一种动物名次的可能性都列举出来,找出同时符合三个条件的组合,就找到答案了。
可以写程序了吗?
编程
我们分别建立小狗、小兔、小猫、小猴和小马五个变量,分别代表这五个动物的名次。然后做四个重复执行的循环,在每一层让小狗、小兔、小猫、小猴依次加上1,在最里面的循环去判断名次,如果三个条件都满足(都为“真”)则显示排名,停止全部脚本(不再进行循环了),最关键的代码如下图所示:
这里有两点还需要解释一下:
- 为什么没有给“小马”变量也建立一个重复执行1-5的循环呢?这是因为,五个动物排名,不管每个动物的名次如何,这些动物的名次加起来都是1+2+3+4+5=15对不对?只要其它四个动物的排名确定了,用15减去他们的排名不就可以得到小马的排名了吗?由于在四个嵌套的重复执行中每次已经确定了其它四个动物的名次组合,所以我们用15-小狗-小兔-小猫-小猴,计算在这种组合下小马的排名,然后用这五个变量去建立逻辑表达式(用“条件1、条件2、条件3”表示),进行判断;
- 为什么让条件1+条件2+条件3=3作为找到答案的依据呢?这是因为在Scratch中,“真”用数字表示就是1,“假”用数字表示就是0,我们把三个布尔值加起来等于3,说明这三个值都是1,没有其它可能性。当然你也可以写成两个“与”嵌套在一起的形式来判断。
至于程序其它的指令,比如让每个动物说明与自己有关的条件,这里就不列出了,相信你可以自己写出来。程序最终运行效果如下,动物们的排名依次是小马、小狗、小兔、小猴、小猫:
总结
用Scratch编程解决逻辑问题时,我们先把问题中的已知条件转换为逻辑表达式,然后采用枚举法,以循环结构将所有可能的方案列举出来,对每种方案判断是否满足所有已知条件的描述,最终找到符合条件的答案(当然,如果题目是错误的,也可能找不到答案)。
最后给出一个思考题,假如小狗说的不是“小马在我的前面问过了终点线”,而是“我在小马的前面问过了终点线,你能编程算出最终的排名吗?