11523| 4
|
[入门教程] 趣味编程指南(5-1)-程序的流程控制-循环语句1 |
本帖最后由 kaka 于 2017-8-18 08:24 编辑 从这节开始,你将会接触到编程中一个重要且强大的知识点 - 循环语句。 在此之前,若你想在程序中画一万个圆,只能用一个可怕的方式去做,写一万行 ellipse。那些千方百计为了提高效率(偷懒)的语言设计者肯定不会允许这样的事发生。所以就有了循环语句。通过它,你可以最直观地感受计算机自动化的威力。 for循环循环语句有多种,其中最常用的就是 for 循环。我们都了解 draw 函数是不断循环执行的。先从开头的第一句开始,由上至下执行直到最后一句,执行完一遍后,又会再从第一句开始重新运行。for 语句和它有点类似。写在它里面的代码都可以被反复执行。 它的语法结构是这样的: [mw_shl_code=applescript,true]for(表达式1; 表达式2; 表达式3){ 循环体 }[/mw_shl_code]循环体中自然就是写我们希望被反复执行的语句。表达式 1 用作初始化,为循环变量赋初值。表达式 2 填写的是循环的条件。表达式 3 会更新循环变量的数值。 循环变量指的是什么?它其实相当于一个局部变量。我们先来看一个完整的写法。 [mw_shl_code=applescript,true]for(int i = 0; i < 10; i++){ 循环体 }[/mw_shl_code]for 语句要实现循环的功能,主要依赖一个局部变量,循环的终止会需要用到它。在上面例子中即是 i。表达式 1 完成了局部变量的初始化。之后循环每执行一遍,这个变量就必须更新。其中实现更新功能部分的就是表达式 3 中的 i++ ,透过它变量每次更新就会增加 1。到最后,循环体中的代码是不能无限循环下去的,否则后面的语句都无法执行,因此我们需要存在一个终止条件,表达式 2 就起到了这个作用。这里程序会判断 i 是否小于 10,当满足就继续执行,不满足就跳出循环。 因此,for循环中的语句执行顺序就是这样的 [mw_shl_code=applescript,true]表达式1(局部变量初始化) 表达式2(满足继续往下执行) 循环体(第一次循环) 表达式3 (更新) 表达式2(满足继续往下执行) 循环体(第二次循环) 表达式3 (更新) 表达式2(满足继续往下执行) 循环体(第三次循环) ... ... 表达式3 (更新) 表达式2(不满足,跳出循环)[/mw_shl_code] 你可以对照这个执行顺序在脑海中模拟几遍。但不亲手敲一次代码是不可能真正理解的。当我们想摸清一个陌生的概念,可以先在控制台通过 println 语句输出数值。 --代码示例(5-1): [mw_shl_code=applescript,true]void setup(){ for(int i = 0; i < 10; i++){ println ("run"); } }[/mw_shl_code] 你可以数一数控制台中输出 run 的数量,这里刚好为 10 个。由此就获悉了循环体中的代码执行的次数。但这样做还是无法察觉循环内具体发生了哪些变化。我们可以把输出的字符 “run” 改成变量 i 试试。 --代码示例(5-2): [mw_shl_code=applescript,true] void setup(){ for(int i = 0; i < 10; i++){ println (i); } }[/mw_shl_code] 现在能看到,在循环体中的 i 值是不断增大的。以后我们可以通过这个值,来了解当前循环的进度。
这样 i 就会恰好对应循环次数。 “<=” 含义是小于等于,所以当 i 等于 10 时会仍然满足条件,因此会比写成 i < 10 在末尾中可多循环一遍。尽管是从 1 开始,但循环的次数一样为 10。当然,若是没有特别必要的话还是建议采取例子开头的写法。后面介绍到的 vector 或数组,都是通过下标来获取元素的,而下标默认都是从 0 开始。初值先设为 0,是较为常用的做法。
不了解大家是否还记得数学家高斯小时候的一则故事。高斯那时 10 岁,他的算术老师想在课堂上布置一个作业,题目是 1+2+3+4……+97+98+99+100=?如果用手算,自然会耗费很长的时间。但高斯估计是自己琢磨出了等差数列的求和方法,所以在题目刚报出时,就轻松地报出答案,让老师大吃一惊。 现在我们可能不大记得等差数列求和究竟是什么了,但却可以用一种原始而暴力的方式得到答案。那就是 for 循环。反正计算对电脑而言,是小菜一碟。我们只要将问题描述为计算机可识别的语言,就能轻松获得答案。 --代码示例(5-3): [mw_shl_code=applescript,true]void setup(){int answer = 0; for(int i = 1; i <= 100; i++){ answer += i; } println(answer); }[/mw_shl_code] 相信你得到的结果会和高斯报出的答案一样,为 5050!
经过一番略显枯燥的铺垫,我们终于可以进入更有意思的环节了。就是用 for 循环画图。那些枯燥的数值计算可以先搁到一边了,设计师还是对图形更为敏感~ 利用 for 循环绘制一排圆当我们想用 for 循环去表现一组重复的元素。只要事先确定这些元素间的数量关系,就能很方便地用 for 循环去实现,而无需做大量的重复劳动。假如要画一排水平方向均匀分布的圆,它的纵坐标是不变的。变化的只是横坐标,而这个横坐标从左到右,是不断递增的,并且递增距离都一样。这时就可以通过 for 循环中的 i,来确定每个圆的横坐标。 --代码示例(5-4): [mw_shl_code=applescript,true]void setup(){ size(700, 700); background(83, 51, 194); noStroke(); } void draw(){ for(int i = 0; i < 7; i++){ ellipse(50.0 + i * 100.0, height/2.0, 80.0, 80.0); } }[/mw_shl_code] 50 代表左面第一个圆的起始位置,i * 100 中的 100 表示递增的距离。 利用 for 循环绘制随机圆点上面图形的位置都是可预测的,这样就失去很多趣味。我们可以用前面提到的 random 函数,来创造更多的可能性。试试将它写在绘图函数中。 --代码示例(5-5): [mw_shl_code=applescript,true]void setup(){size(700, 700); background(0); noStroke(); } void draw(){ background(0); for(int i = 0; i < 10; i++){ float randomWidth = random(60.0); ellipse(random(width), random(height), randomWidth, randomWidth); } }[/mw_shl_code] 这里的圆的位置之所以不断闪动,是因为 random 函数每执行一次,结果都是随机的。由于 draw 函数默认每秒运行 60 帧,所以每次绘制的 10 个圆在一秒内,会随机改变 60 次位置。这种快速的闪动就让画面看上去不止有 10 个圆了。 程序里改变一个简单的数值,就能获得截然不同的效果。我们可以通过修改循环终结条件来改变循环的次数。下图的循环终结条件为 i < 100 下面是循环终结条件为 i < 1000 时的效果randomSeed 如果我不希望圆的位置是随机生成的,但又不希望它闪动。应该怎么做?一种做法是为每个圆的坐标都创建独立的变量去保存,并在 setup 中对这些变量进行初始化,一并赋上随机值。这样在 draw 里使用绘图函数,调用的就是变量中存储的数值,而不会随时发生改变。 画 10 个圆尚且可用这个方法去创建变量。但画 1000 个圆,10000 个圆。要创建这个数量的变量用传统的方式逐个去命名是会非常繁琐的。 我们先不用去学习新的创建变量的方式。这里有一个灵活的方法可以达到这个目的。就是使用 randomSeed。先看使用后的效果。 --代码示例(5-6): [mw_shl_code=applescript,true]void setup(){ size(700, 700); background(0); noStroke(); } void draw(){ background(0); randomSeed(1); for(int i = 0; i < 10; i++){ float randomWidth = random(20.0, 60.0); ellipse(random(width), random(height), randomWidth, randomWidth); } }[/mw_shl_code] 与前面的代码相比没有很大变化,除了让圆的半径随机范围改成从 20 到 60 之外,只多了一句 randomSeed。但用上这句之后,图形似乎都变静止了。 调用格式: randomSeed(a);其中的 a 设置的是种子,需要填写整数(在 P5 中填写浮点数不会报错,但只会将它化成整数处理)。randomSeed 的作用是设定随机数的种子,它会根据不同的种子,生成不同的随机数序列。在它之后调用的 random 函数,返回的结果都是确定的。这里的确定不是指结果是一个定值,它确定的只是生成的序列。也就是说对应的调用次数,返回的结果是确定的。 --代码示例(5-7): [mw_shl_code=applescript,true]void setup(){ randomSeed(0); for(int i = 0; i < 5; i++){ println(random(10)); } }[/mw_shl_code] 继续用 println 做个实验。使用了 randomSeed 后,你每次关闭程序,再运行程序。返回的都会是一串同样的结果。数值与顺序会一一对应的。去掉的话,每次返回的都是不同的数值。 之所以有这种设置。是因为程序中的随机数,本身都是“伪随机”的。结果看似随机,实则都是通过一个固定的,可重复的计算方法产生的。randomSeed 就相当于指定某个原始值。之后的结果都会根据这个种子来推算。而当我们不指定种子时,程序会默认根据系统的当前时间来生成种子。因此每次运行结果都不一样。 下面的例子可以帮助你更好地理解 randomSeed。 --代码示例(5-8): [mw_shl_code=applescript,true] void setup(){ size(700, 700); background(0); noStroke(); } void draw(){ randomSeed(1); for(int i = 0; i < 10; i++){ float randomWidth01 = random(10, 60); ellipse(random(width), random(height), randomWidth01, randomWidth01); println(randomWidth01); } randomSeed(1); for(int i = 0; i < 10; i++){ float randomWidth02 = random(10, 60); ellipse(random(width), random(height), randomWidth02, randomWidth02); println(randomWidth02); } }[/mw_shl_code] 尝试将第二个 randomSeed(1) 修改成 randomSeed(0) 对比最终的结果。
--代码示例(5-9): [mw_shl_code=applescript,true]void setup(){ size(700, 700); background(0); } void draw(){ randomSeed(0); for(int i = 0; i < 2000; i++){ float x1 = width/2.0; float x2 = random(50.0, 650.0); stroke(255, 20); line(x1, 50, x2, 650); } }[/mw_shl_code] 转自www.inslab.cn |
© 2013-2024 Comsenz Inc. Powered by Discuz! X3.4 Licensed