kaka 发表于 2017-2-21 13:57:01

趣味编程指南(5-1)-程序的流程控制-循环语句1

本帖最后由 kaka 于 2017-8-18 08:24 编辑


从这节开始,你将会接触到编程中一个重要且强大的知识点 - 循环语句。在此之前,若你想在程序中画一万个圆,只能用一个可怕的方式去做,写一万行 ellipse。那些千方百计为了提高效率(偷懒)的语言设计者肯定不会允许这样的事发生。所以就有了循环语句。通过它,你可以最直观地感受计算机自动化的威力。for循环循环语句有多种,其中最常用的就是 for 循环。我们都了解 draw 函数是不断循环执行的。先从开头的第一句开始,由上至下执行直到最后一句,执行完一遍后,又会再从第一句开始重新运行。for 语句和它有点类似。写在它里面的代码都可以被反复执行。它的语法结构是这样的:    for(表达式1; 表达式2; 表达式3){      循环体    }循环体中自然就是写我们希望被反复执行的语句。表达式 1 用作初始化,为循环变量赋初值。表达式 2 填写的是循环的条件。表达式 3 会更新循环变量的数值。循环变量指的是什么?它其实相当于一个局部变量。我们先来看一个完整的写法。    for(int i = 0; i < 10; i++){      循环体    }for 语句要实现循环的功能,主要依赖一个局部变量,循环的终止会需要用到它。在上面例子中即是 i。表达式 1 完成了局部变量的初始化。之后循环每执行一遍,这个变量就必须更新。其中实现更新功能部分的就是表达式 3 中的 i++ ,透过它变量每次更新就会增加 1。到最后,循环体中的代码是不能无限循环下去的,否则后面的语句都无法执行,因此我们需要存在一个终止条件,表达式 2 就起到了这个作用。这里程序会判断 i 是否小于 10,当满足就继续执行,不满足就跳出循环。因此,for循环中的语句执行顺序就是这样的表达式1(局部变量初始化)
    表达式2(满足继续往下执行)
    循环体(第一次循环)
    表达式3 (更新)
    表达式2(满足继续往下执行)
    循环体(第二次循环)
    表达式3 (更新)
    表达式2(满足继续往下执行)
    循环体(第三次循环)
    ...
    ...
    表达式3 (更新)
    表达式2(不满足,跳出循环)你可以对照这个执行顺序在脑海中模拟几遍。但不亲手敲一次代码是不可能真正理解的。当我们想摸清一个陌生的概念,可以先在控制台通过 println 语句输出数值。--代码示例(5-1):void setup(){      
      for(int i = 0; i < 10; i++){
            println ("run");
      }
    }你可以数一数控制台中输出 run 的数量,这里刚好为 10 个。由此就获悉了循环体中的代码执行的次数。但这样做还是无法察觉循环内具体发生了哪些变化。我们可以把输出的字符 “run” 改成变量 i 试试。--代码示例(5-2):    void setup(){      
      for(int i = 0; i < 10; i++){
            println (i);
      }
    }现在能看到,在循环体中的 i 值是不断增大的。以后我们可以通过这个值,来了解当前循环的进度。
[*]在示例(5-2)中,i 的值是从 0 变化到 9。似乎和实际循环次数总是相差 1。若是觉得不习惯,for语句括号中的表达式也可以写成
   for(int i = 1; i <= 10; i++)这样 i 就会恰好对应循环次数。 “<=” 含义是小于等于,所以当 i 等于 10 时会仍然满足条件,因此会比写成 i < 10 在末尾中可多循环一遍。尽管是从 1 开始,但循环的次数一样为 10。当然,若是没有特别必要的话还是建议采取例子开头的写法。后面介绍到的 vector 或数组,都是通过下标来获取元素的,而下标默认都是从 0 开始。初值先设为 0,是较为常用的做法。
[*]上例中若写 i 大于 0,程序是会崩溃的。因为变量持续递增,永远都会满足这个条件。这样相当于不能终止,程序会陷入死循环。
[*]for语句中的局部变量不仅可以声明整形类型,还可以用诸如浮点类型来声明变量。可以写成 for(float i = 0;i < 10;i+=0.02)。
for循环解数学题不了解大家是否还记得数学家高斯小时候的一则故事。高斯那时 10 岁,他的算术老师想在课堂上布置一个作业,题目是1+2+3+4……+97+98+99+100=?如果用手算,自然会耗费很长的时间。但高斯估计是自己琢磨出了等差数列的求和方法,所以在题目刚报出时,就轻松地报出答案,让老师大吃一惊。现在我们可能不大记得等差数列求和究竟是什么了,但却可以用一种原始而暴力的方式得到答案。那就是 for 循环。反正计算对电脑而言,是小菜一碟。我们只要将问题描述为计算机可识别的语言,就能轻松获得答案。--代码示例(5-3):   void setup(){
      int answer = 0;      
      for(int i = 1; i <= 100; i++){
            answer += i;
      }
      println(answer);
    }相信你得到的结果会和高斯报出的答案一样,为 5050!
[*]Tips:for 循环中局部变量的名字可以随意更改,只要符合变量的命名规则即可。可以写成 (int k = 1;k <= 100;k++)。没有特殊情况,默认使用 i 作为变量名。
for循环绘图经过一番略显枯燥的铺垫,我们终于可以进入更有意思的环节了。就是用 for 循环画图。那些枯燥的数值计算可以先搁到一边了,设计师还是对图形更为敏感~利用 for 循环绘制一排圆当我们想用 for 循环去表现一组重复的元素。只要事先确定这些元素间的数量关系,就能很方便地用 for 循环去实现,而无需做大量的重复劳动。假如要画一排水平方向均匀分布的圆,它的纵坐标是不变的。变化的只是横坐标,而这个横坐标从左到右,是不断递增的,并且递增距离都一样。这时就可以通过 for 循环中的 i,来确定每个圆的横坐标。--代码示例(5-4):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);      
      }
    }50 代表左面第一个圆的起始位置,i * 100 中的 100 表示递增的距离。利用 for 循环绘制随机圆点上面图形的位置都是可预测的,这样就失去很多趣味。我们可以用前面提到的 random 函数,来创造更多的可能性。试试将它写在绘图函数中。--代码示例(5-5):    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);
      }
   }


这里的圆的位置之所以不断闪动,是因为 random 函数每执行一次,结果都是随机的。由于 draw 函数默认每秒运行 60 帧,所以每次绘制的 10 个圆在一秒内,会随机改变 60 次位置。这种快速的闪动就让画面看上去不止有 10 个圆了。程序里改变一个简单的数值,就能获得截然不同的效果。我们可以通过修改循环终结条件来改变循环的次数。下图的循环终结条件为 i < 100下面是循环终结条件为 i < 1000 时的效果

randomSeed 如果我不希望圆的位置是随机生成的,但又不希望它闪动。应该怎么做?一种做法是为每个圆的坐标都创建独立的变量去保存,并在 setup 中对这些变量进行初始化,一并赋上随机值。这样在 draw 里使用绘图函数,调用的就是变量中存储的数值,而不会随时发生改变。
画 10 个圆尚且可用这个方法去创建变量。但画 1000 个圆,10000 个圆。要创建这个数量的变量用传统的方式逐个去命名是会非常繁琐的。
我们先不用去学习新的创建变量的方式。这里有一个灵活的方法可以达到这个目的。就是使用 randomSeed。先看使用后的效果。
--代码示例(5-6):
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);
      }
         }

与前面的代码相比没有很大变化,除了让圆的半径随机范围改成从 20 到 60 之外,只多了一句 randomSeed。但用上这句之后,图形似乎都变静止了。



调用格式:
      randomSeed(a);其中的 a 设置的是种子,需要填写整数(在 P5 中填写浮点数不会报错,但只会将它化成整数处理)。randomSeed 的作用是设定随机数的种子,它会根据不同的种子,生成不同的随机数序列。在它之后调用的 random 函数,返回的结果都是确定的。这里的确定不是指结果是一个定值,它确定的只是生成的序列。也就是说对应的调用次数,返回的结果是确定的。
--代码示例(5-7):
void setup(){
               randomSeed(0);
            for(int i = 0; i < 5; i++){
                println(random(10));
            }
      }



继续用 println 做个实验。使用了 randomSeed 后,你每次关闭程序,再运行程序。返回的都会是一串同样的结果。数值与顺序会一一对应的。去掉的话,每次返回的都是不同的数值。
之所以有这种设置。是因为程序中的随机数,本身都是“伪随机”的。结果看似随机,实则都是通过一个固定的,可重复的计算方法产生的。randomSeed 就相当于指定某个原始值。之后的结果都会根据这个种子来推算。而当我们不指定种子时,程序会默认根据系统的当前时间来生成种子。因此每次运行结果都不一样。
下面的例子可以帮助你更好地理解 randomSeed。
--代码示例(5-8):
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);
      }
}
尝试将第二个 randomSeed(1) 修改成 randomSeed(0) 对比最终的结果。

[*]Tips:P5 中只要在 draw 的结尾调用 noLoop 函数,就可以达到同样的效果,它的作用是令程序中止运行。与上面的实现原理有本质的区别。
for 循环画线 掌握 randomSeed 的用法之后,可以尝试替换绘图函数,例如将画圆变成画线。只要给直线的端点设计一些变化规则,就可用大量的线条交织出独特图案。
--代码示例(5-9):
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);
      }
}



转自www.inslab.cn

hnyzcj 发表于 2017-2-21 19:15:58

雪花点好动人

iooops 发表于 2017-2-28 19:34:56

板凳

iooops 发表于 2017-2-28 19:34:59

地板

iooops 发表于 2017-2-28 19:35:05

好666
页: [1]
查看完整版本: 趣味编程指南(5-1)-程序的流程控制-循环语句1