在这里,我们将介绍一个新游戏--Pond Tutor
在Pond Tutor(https://blockly-games.appspot.com/pond-tutor)这个游戏中,我们将扮演黄色的鸭子,通过不断的发炮弹去攻击红色的鸭子,当红色的鸭子血条减为0时则玩家获胜。
在这个游戏中为我们提供了四个功能模块:
Swim模块:向给定方向游动;
Scan模块:向给定方向扫描,扫描到敌方时返回二者相距的距离;
Cannon模块:向给定方向和距离发射炮弹;
Stop模块:配合swim模块,使我方停止。
通过结合这几个模块,我们可以想到:首先我们需要朝敌方游动,同时不断的进行扫描;当扫描到敌方时,我们应该停止,同时用扫描到的距离设定炮口的攻击距离。
这里唯一的问题是如何重复不断的进行扫描,所以也就牵出了我们接下来要讲的问题--循环。
循环是一种基本的程序结构,凡是需要通过不断重复执行才能得到答案的许多问题中需要用到循环控制。循环结构是结构化程序设计的基本结构之一,它和顺序结构、选择结构共同作为各种复杂程序的基本构造单元。因此熟练掌握选择结构和循环结构的概念及使用是程序设计的最基本的要求。本章将介绍四种形式的循环,都是程序中基本的用于表示重复动作的结构。在本章学习结束后,学生应当具备的能力有:分析一个循环的重复执行次数和终止条件,并能在自己的程序中应用到重复次数,重复,步长,列表循环循环。
5.1 基本概念
迭代:通过包括重复序列允许通过一个简短的程序来代替一系列的重复的步骤程序中重复执行的一段指令叫做循环。
每一个循环由两部分组成:
条件: 控制循环重复次数 循环体: 循环执行时始终运行的代码段。 我们称一个无休止运行的程序为死循环,下图所示的就是一个典型的死循环块。死循环将会阻止一个程序完成其执行,所以含有死循环的程序通常是不可取的。 在浏览器内,一个死循环甚至可以导致程序对鼠标点击失去响应或无限输出对话框,因此要避免出现死循环,控制循环执行次数的程序必须编写正确。
5.2 重复次数模块
重复次数模块用来实现计数循环,通过在模块中修改次数来规定重复执行的次数。
例如我们要循环三次输出“Hello World”,则只要在重复次数模块的数字块中写入“3”即可。
5.3 重复模块
重复模块中包含了两种不同类型的循环模式:重复-当和重复-直到。
重复-当模块用来实现“当型”循环结构,只要程序的执行条件为真,就会重复执行语句的一系列程序。在未运行前,实际的重复次数是未知的。
它需要与逻辑语句组合使用,当条件满足时它将重复不断的执行直到条件不满足,所以可能出现的情况是:由于一个逻辑错误,循环将相应代码段重复运行无数次,这对初学者来说是很常见的。
当检查一个循环条件时循环将会执行。如果循环条件为真,将执行循环体中的代码并重复执行这些步骤,在每次循环前将额外检查循环条件。条件为假时退出循环。
循环程序都包涵了三个内容:
一个清晰的起始条件
一个循环条件,指示程序是否应该继续执行循环部分
*一个条件改变最终导致循环条件变为假。
例如,我们使用重复-当模块来实现一个非常常见的问题:从1一直加到100。对于这问题同学们一定不陌生,甚至能有多种不同的技巧去解这个问题。但在这里,我们使用循环就可以用最笨的方法:从1一个一个的加起来,一直加到100。
而对于重复-直到这个模块来说则与重复-当模块刚好相反:它是当条件满足时就结束循环。
所以同样针对上面从1加到100的例子,我们只需要将条件i<=100改为i>100即可。
5.4 步长循环模块
步长循环模块使用最为灵活,不仅可以用于循环次数已经确定的情况,而且可以用于循环次数不确定而只给出循环结束条件的情况,它完全可以替代重复模块。
在固定增量下有着固定循环次数的一段程序使用步长循环模块为宜。下面的循环将输出三次单词“Hello”。
如果你已经接触过一些高级程序设计语言如:C/C++/Java等,你会发现步长循环模块其实就是其中的For循环语句。
步长循环模块中“使用”后的k为循环的控制变量,这个k可以自己命名并且可以在循环块内充当变量使用;“从范围”后的数字代表循环控制变量k的初始值;“到”后的数值表示循环的结束条件:当k大于这个值时退出循环;“每隔”后的数字为k的步长,表示每循环一次,k增加多少。以上所有的数值都是可以根据自己的实际需要进行修改的。
下面我们大家都很熟悉的一道应用题来使用步长模块:一球从100米高度自由落下,每次落地后反跳回原高度的一半,再落下。求它在第10次落地时,共经过多少米?第10次反弹多高?
在这里我们并没有从第一次下落开始就使用循环,而是以落点-上升-落点为过程进行的,由于落点-上升这一过程与上升-落点的过程所经过的距离相同,所以总路程直接加二倍的反弹距离;而反弹距离直接除2即可直接变为原来的一半。
5.5 列表循环模块
列表循环模块是对列表(list)中每一个元素进行循环迭代的模块,也可以称为是对列表元素的遍历。所谓列表,是相同数据类型的元素按一定顺序排列的集合。使用列表循环模块的循环在达到列表的最后一个值后将自动结束。所以使用列表循环模块是不太可能错误地写成死循环的,或许唯一可以的方式是为循环使用一个含有无限元素的列表!
如下图所示,通过使用列表循环模块可以输出列表numList中的每一个元素。
虽然使用重复、步长模块也能实现循环输出每一个元素,但用这种方法不但需要知道列表的长度,而且无法像列表循环模块一样直接就定义好了变量去代表列表的元素。
例如求一个列表中的最大元素,我们可以用列表循环模块来实现:
在这里我们定义了一个变量tmp去保存最大的值,将tmp初始化为0保证tmp刚开始为最小,之后用列表循环模块循环拿出列表中的元素不断的与tmp相比较,一旦当前值大于tmp则修改tmp为这个值。最终在tmp中的值将是最大的值。
5.6 中断/继续模块
中断模块可以用来从循环体内跳出循环体,即提前结束循环,接着执行循环下面的语句。
中断模块适用于当我们不知道循环次数,只有在程序执行过程中满足一定条件的情况下要结束循环的时候。
我们现在尝试使用循环和中断模块来写一个程序:输入一个数,判断这个数是否为素数。
在这里我们让m被2到根号m除,如果m能被2到根号m之中的任何一个整数整除,则提前结束循环,此时i必然小于或等于k(即根号m);如果m不能被2到k(即根号m)之间的任一整数整除,则在完成最后一次循环后,i还要加1,因此i=k+1,然后才终止循环。在循环之后判别i的值是否大于或等于k+1,若是,则表明未曾被2到k之间任一整数整除过,因此输出“是素数”。
继续模块其作用为结束本次循环,即跳过循环体中下面尚未执行的语句,接着进行下一次是否执行循环的判定。
继续模块与中断模块的区别是:继续模块只结束本次循环,而不是终止整个循环的执行。而中断模块则是结束整个循环过程,不再判断执行循环的条件是否成立。
如把100-200之间的不能被3整除的数输出来:
当n能被3整除时,执行继续模块,结束本次循环(即跳过print模块),只有n不能被3整除时才执行print模块输出数字。
5.7 嵌套循环
一个循环体内又包含另一个完整的循环结构,称为循环的嵌套。内嵌的循环中还可以嵌套循环,这就是多层循环。各种语言中关于循环的嵌套的概念都是一样的。
四种循环(重复次数,重复,步长,列表循环)可以互相嵌套,当循环嵌套在循环中,一个程序可以创建一个重复的重复。
5.7.1 内循环和外循环
当使用嵌套循环时,内外循环扮演着不同的角色:
外循环是先开始后结束的运行顺序。它在程序运行中开始一次结束一次。内循环是比外循环后开始先结束的运行。它可以反复地重复运行。
内循环快速运行,每一次外循环,都会完成一个完整的内循环。外循环循环的偏慢。
内部和外部的循环类似时钟指针的运转。例如,一个时钟的秒针就像内部循环同时分针就像外循环.对于每一次分针的转动,秒针都需要转一圈,转动60整秒.当分针转动一整个小时时,秒针将转动60*60 = 3600秒。里程表和日历的都是用这样相似的工作方式。
5.7.2 非独立的内循环
有时,内部循环的工作方式取决于外部循环的哪一步运行。例如,创建一个程序打印在一年中所有的日期,您可以使用嵌套循环去打印月份的每天,但内部循环的次数取决于外部循环,这是因为每个月有不同的天数。
在这里,内部循环重复的是不同的次数,这取决于它在哪个月份上,因为它的结束条件是 j<days[i].
对于月份,我们绘制了一个依赖于存放每一月的天数的数组的内循环,但是对于给一个非独立内循环构思一个规则需要更加的泛化:这样一个程序员需要考虑使用的特殊情况,同时要尝试泛化这个规则。
由于内部循环和外部循环的关系,嵌套循环可以使具有挑战性的程序正确。设计嵌套循环的一个很好的策略是在一个例子中仔细地找出你想要的运行效果,然后找到一个方法来归纳它。
课后练习
1. 写一个判断素数的函数,在主函数输入一个整数,输出是否是素数。
2. 设计定义一个自己的工具块。
3. 编写函数,给出年、月、日,计算该日是该年的第n天,并尝试将其导出的代码在其他语言环境中调试运行。