Oh!Coder

Coding Life

有趣的Processing-初识数组

| Comments

今天呢,我们一起来学习Processing中的数组。关于这部分的学习,我们打算分两次进行,这一次我们主要学习数组的基本概念,下一次,我们会在此集出上,学习关于数组在应用层面上的知识。好,还是少啰嗦,让我们马上进入正题!

什么是数组

首先,让我们先来看看什么是数组?数组么,顾名思义,本质上就是一系列数据的组合。对于这一系列数据的组合呢,其中每个数据都是独立的,可以对每个单独的数据进行分配和读取,然而这一些列数据必须是同一种类型,不能属于不同类型。嗯,这样干巴巴的解释,听起来有些过于概念化,还是让我们用具体的例子来说明更容易理解一些。

例如,我们想画出十个长条矩形,以此来表示十个数据。如果我们不使用数组,大概我们要写出很多看起来是重复的代码。具体,我们还是打开Processing的PDE,新建一个项目窗口。

processing-new-window

图片来源:Processing新项目窗口

然后,我们大概会向其中添入如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 环境初始化。
// 此方法在软件启动时,被系统调用一次。
void setup(){
  // 此处无代码。开心不?
}
// 画图。
// 此方法被系统默认循环调用。
void draw(){
  // 定义十个整型数,
  // 作为矩形的长度
  int x0 = 50;
  int x1 = 61;
  int x2 = 83;
  int x3 = 69;
  int x4 = 71;
  int x5 = 50;
  int x6 = 29;
  int x7 = 31;
  int x8 = 17;
  int x9 = 39;
  // 设置矩形填充色为黑色
  fill(0);
  // 下面十行代码分别画出十个矩形
  rect(0, 0, x0, 8);
  rect(0, 10, x1, 8);
  rect(0, 20, x2, 8);
  rect(0, 30, x3, 8);
  rect(0, 40, x4, 8);
  rect(0, 50, x5, 8);
  rect(0, 60, x6, 8);
  rect(0, 70, x7, 8);
  rect(0, 80, x8, 8);
  rect(0, 90, x9, 8);
}

最后,我们点击项目窗口菜单栏的三角形按钮(如下图所示),编译并运行这段代码。

processing-window-menu

图片来源:Processing新项目窗口中的菜单栏

上述代码运行的结果,看起来像是这个样子:

processing-fun-intro-array-1

图片来源:Processing程序执行结果

很显然,上述例子中的代码,不管是int类型的定义,还是调用画矩形图rect()方法,都做了很多看起来不断重复的工作。如果我们学了数组,那么上述代码就会大幅度缩减,瞬间会变成下面这个样子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 环境初始化。
// 此方法在软件启动时,被系统调用一次。
void setup(){
  // 此处无代码。开心不?
}
// 画图。
// 此方法被系统默认循环调用。
void draw(){
  // 定义整型数组x,
  // 其中包含十个整数
  int[] x = {
    50, 61, 83, 69, 71, 50, 29, 31, 17, 39
  };
  // 设置矩形的填充色为黑色
  fill(0);
  // 以数组x包含的数据个数为准,
  // 画出个数与x数组数据个数相同的矩形,
  // 此处x数组包含的数据个数为十个
  for (int i = 0; i < x.length; i++) {
    // 画出矩形
    rect(0, i*10, x[i], 8);
  }
}

同样按照上述步骤,编译并运行这段代码,如果不出意外,它们的运行结果是一样的。然而代码长度却大幅度降低,少了很多重复代码。好!现在呢,我们既从概念上又从实际操作中对数组有了一个初步的印象。那接下来,就让我们看看,如何定义一个数组吧!

定义一个数组

要想定义一个数组,按步骤划分,可以分为三个步骤。具体来说,包括声明、创建以及分配。声明,意味着向程序编译器说明,我们想要一个什么类型的数组,相较于普通类型而言,声明数组类型要在类型后面紧跟[]符号。创建,需要关键字new来完成,意味着要为所声明的数组,分配相应的数据空间,以此来存储数据,new后面要跟数据类型以及要分配的数据个数,比如data = new int[5]。分配,是要向已经创建的空间,存储相应的数据。

对于完成上述三个步骤,也有三种对应的方式。下面,让我们用代码来演示,具体如何完成。

方式一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 声明类型为int的数组data
// 数组的个数为5
int[] data;
// 环境初始化。
// 此方法在软件启动时,被系统调用一次。
void setup() {
  // 设置窗口的大小
  size(100, 100);
  // 创建数组data
  data = new int[5];
  // 以下五行代码,
  // 分别为创建的数组data
  // 依次分配数据
  data[0] = 19;
  data[1] = 40;
  data[2] = 75;
  data[3] = 76;
  data[4] = 90;
}
// 画图。
// 此方法被系统默认循环调用。
void draw(){
  // 此处为其他图形显示代码
}

方式一,将声明、创建以及分配分为三个步骤,最终完成数组的定义。

方式二

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 声明,并创建数组data
// 数组的个数为5
int[] data = new int[5];
// 环境初始化。
// 此方法在软件启动时,被系统调用一次。
void setup() {
  // 设置窗口的大小
  size(100, 100);
  // 以下五行代码,
  // 依次分配数据
  data[0] = 19;
  data[1] = 40;
  data[2] = 75;
  data[3] = 76;
  data[4] = 90;
}
// 画图。
// 此方法被系统默认循环调用。
void draw(){
  // 此处为其他图形显示代码
}

方式二,可以看出,将数组data的声明和创建合并为了一步,只有分配这一步单独执行。

方式三

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 将数组data的声明,
// 创建以及分配合并为一步
int[] data = { 19, 40, 75, 76, 90 };
// 环境初始化。
// 此方法在软件启动时,被系统调用一次。
void setup() {
  // 设置窗口的大小
  size(100, 100);
}
// 画图。
// 此方法被系统默认循环调用。
void draw(){
  // 此处为其他图形显示代码
}

方式三,将声明、创建以及分配合并为了一步,只有一行代码。

总述上面三种定义一个数组的方式,我们可以看到,第一种方式,是将三步拆分开,一步一步的完成。第二种方式,是将声明和创建合并为一步,将分配作为单独的一步。第三种方式,将三步直接合并成为一步。

读取数组数据

既然我们已经学会了定义数组,那如何读取数组中的数据呢?下面,我们看看如何读取数组中的数据。

对于已经定义好的数组,我们可以通过数组名+[]+数组索引的方式读取。所谓数组索引,是指数组当中某个元素所排的具体位置。数组的元素位置从数字0开始算起。下面,还是让我们以代码来具体说明。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 环境初始化。
// 此方法在软件启动时,被系统调用一次。
void setup(){
  // 定义数组 data
  int[] data = { 19, 40, 75, 76, 90 };
  // 调试窗口中打印出 data[0] 为 19
  println(data[0]);
  // 调试窗口中打印出 data[1] 为 75
  println(data[2]);
  // 调试窗口中打印出 data[5] 为 ERROR
  println(data[5]);
}
// 画图。
// 此方法被系统默认循环调用。
void draw(){
  // 此处为其他图形显示代码
}

上述代码中,可以看到,data[0]可以获取数组的第一个数值19。data[2]可以获取数组的第三个数值75。因为data数组的个数只有5个,又因为数组索引从0开始,所以data[4]为data数组的最后一个数值90,而若访问data[5],则超出了数组的总个数。所以上述示例中,当调用println(data[5])的时候,因为找不到data[5]所以程序会报错。

介绍完了数组数据的读取,按理说,接下来应该介绍一下,如何将数据实时的存储到数组中了。嗯,猜对了!

将数据记录到数组

关于如何将数据存储到数组当中,我们还是以实际的例子来说明。虽说Processing为我们提供了pmouseXpmouseY变量,但也仅限于上一次的鼠标位置,再多了,就无法记录了。那么接下来的这个例子,我们将实时并连续的记录鼠标的坐标位置,并以鼠标的坐标值为基础,并将其画成线,实时的显示在窗口中。好,还是让我们通过代码,来看看具体如何实现吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 声明数组y
// 类型为int
int[] y;
// 环境初始化。
// 此方法在软件启动时,被系统调用一次。
void setup() {
  // 设置窗口大小
  size(100, 100);
  // 创建数组y,
  // 大小为窗口宽度,
  // 此处为100,
  // 也即y数组的元素个数为100
  y = new int[width];
}
// 画图。
// 此方法被系统默认循环调用。
void draw() {
  // 设置背景色
  background(204);
  // 不断的将y数组中前一个元素的值赋值给
  // 后一个元素
  for (int i = y.length-1; i > 0; i--) {
    // 将前一个元素的数值赋值给后一个元素
    // 也即将前一个元素向后移动一个位置,
    // 将数组最后一个元素丢弃
    y[i] = y[i-1];
  }
  // 实时获取鼠标Y轴坐标
  y[0] = mouseY;
  // 不断的画线。
  // 此处line()方法有四个参数,
  // 前两个参数是起始点的x1,y1
  // 后两个参数是终点的x2,y2
  // 注意此处起始点在终止点的右边。
  // 例如x1为1时,x2为x1-1即为0
  // y1为y[1]时,y2为y[0]即为mouseY,以此类推
  // 数组y,每两个数值为一组,
  // 分别为line()方法的起始点和终止点的y1和y2数值
  for (int i = 1; i < y.length; i++) {
    // 画线段
    line(i, y[i], i-1, y[i-1]);
  }
}

编译,并运行上述代码,如果一切顺利,最终效果看起来类似于下图所示。

processing-fun-intro-array-2

图片来源:Processing程序执行结果

上述例子中,有两个关键点,第一个是y[0] = mouseY,第二个是y[i] = y[i-1],前一个赋值语句,是更新y[0]的值,后一个赋值语句,是更新整个数组y的值,不断的将y数组中的前一个值覆盖后一个值。有一个地方有点绕,那就是line(i, y[i], i-1, y[i-1])这句,因为,前两个参数x1和y1的数值显示位置,通常在后两个参数x2和y2的前面,这里正好相反,需要大家注意一下。不过,总的来说,可以看到,更新整个数组的数值,通常使用for语句来完成。

总结

这一次,我们对于数组的学习,就先到这里。通过这次的学习,我们学习了什么是数组,数组的定义,以及对于数组元素数据的读取和存储。每个部分,我们也都通过代码做了举例,大家通过代码例子,可以帮助大家有一个更直观的理解。

下期预告

这次呢,我们只是对于数组有了初步的学习。下一次,我们将对数组有更进一步的学习!

Comments