数组

所谓数组(Array),是有序的元素序列。说的通俗一点,“数组”和高中数学的“数列”基本是一回事。一个数组内包含多个元素(GML 内指实数和字符串,GML 外还有更多类型),且这些元素是有排列顺序的。

来回忆一下高中数学的数列:

一个数列 {Xn},它所含的不同元素用下标来区分,即以 X1X2X3 来表示数列 {Xn} 的第一项,第二项,第三项元素。在解题时,我们可能不会直接使用数字下标,而是形如:X(m+2) = 2Xm,通过未知数 m 来表示下标,以便于求解 m。如果我们解出 m = 6,那么很显然,在这里 X8 = 2X6,即:X(m+2)X8 都表示数列 {Xn} 的第八项元素。

数组也是相同的道理。

数组元素的表达形式是:数组名[表达式]。即,若存在一个数组 num,那么它的元素用 num[5]num[12] 这样来表示。因此,[] 被称为下标运算符

注意:数组的第一个元素是 num[0] 不是 num[1]。不仅是数组,几乎所有的索引都是从 0 开始,而不是从 1 开始。从零开始的索引是几乎所有语言的通性,习惯这一点对你学习更高等的语言有很大的帮助。

与数列相同的,数组元素的下标也可以用未知数和表达式表示。如:

num[8 - 3 * 2]num[2] 是同一个元素,若 m 为 6,则 num[m - 3]num[3] 是同一个元素。

也可以将返回值是整数的函数作为下标:

num[irandom(8)] 会随机挑选 num[0]num[8] 之间的某个元素。

数组元素的一般性

数组元素在代码中的地位与普通变量完全相同。即,普通变量能做的,数组元素都能做。

赋值

1
2
3
num[0] = 5;
num[1] = num[0] + 2;
num[2] = num[1] * num[0];

运算

1
2
3
a = 3;
num[0] = irandom(6);
b = a + num[0] * 6;

逻辑判断

1
2
3
4
num[0] = 12;
num[1] = 14;
if (num[0] > num[1] && num[0] > 0)
    num[0] -= num[1];

作为函数参数

1
2
3
4
xx[0] = 100;
xx[1] = 200;
instance_create(xx[0], 100, objBullet);
instance_create(xx[1], 100, objBullet);

作为脚本的返回值

1
2
3
4
5
6
7
8
// 这是一个脚本
{
    var _array;    // 声明临时的数组,只需要声明数组名
    _array[0] = argument0;
    _array[1] = argument1;
    _array[2] = _array[0] + _array[1];
    return _array[2];
}

作为全局数组

1
2
3
4
global.BOSS[0] = 1;
globalvar BOSS;    // 同样只需要声明数组名
BOSS[0] = 0;       // global.BOSS[0] 和 BOSS[0] 是同一个数组元素
show_message(string(global.BOSS[0]));   // 此时输出 0 而不是 1

数组元素的特殊性

说到数组元素的特殊性,毫无疑问就是数组的下标可以用表达式表示。这个特性可以方便我们做很多事。例如:

1
2
for (i = 0; i < 10; i += 1)
    inst[i] = instance_create(400, 400, objBullet);

根据 for 语句的效果,将在房间中创建 10 个 objBullet 的实例,并且这十个实例的索引(ID)分别被储存在 inst[0]inst[9] 中,这样就可以分别控制十个实例。例如:

1
2
for (j = 0; j < 3; j += 1)
    inst[j + 2].speed = 6;

这样,inst[2]inst[3]inst[4] 所表示的三个实例,就会以速度 6 开始运动。

二维数组

所谓二维数组,就是通过两个值来确定一个数据,比如 a[5, 3],在方括号内用逗号隔开。二维数组可以当做是一个书架,a[5, 6] 指第五行,第六列的书(数据)。二维数组的使用等同于一维数组,但是使用二维数组能更好的控制平面的内容。比如在房间内均匀创建 6x6 一共 36 个 objBullet,那么就可以用二维数组来分别储存控制这 36 个实例:

1
2
3
for (i = 0; i < 6; i += 1)
    for (j = 0; j < 6; j += 1)
        inst[i, j] = instance_create(i * 100, j * 100, objBullet);

注意 inst 数组的取值范围是 inst[0~5, 0~5]

GM 数组的特殊性

在 GM8 中,务必记住一个特点:数组名等效于零号元素

即,对于任意的一个变量名而言,aa[0] 代表同一个变量。即使 a 本身并不是一个数组,你仍然可以用 a[0] 来表示它。不仅是自定义的变量,GM8 内置变量也是如此,如 view_xviewview_xview[0] 完全等效(该变量参见视野)。因此,GM8 的脚本并不能传递数组,即使将数组名作为参数或者返回值,实际上只是传递了零号元素的值罢了。如果想要通过脚本的参数或者返回值传递一列数值,请参考数据结构

而在 GMS2 中,存在“重定义”(或覆盖定义)的概念,即在已经定义 a 的情况下,定义 a[] 会重定义 a 为数组,那么原本变量 a 会被删除,之后 a 就只能作为数组名来使用了,反过来说,定义 a 也会覆盖定义 a[]。在 GMS2 中脚本可以传递数组,但是必须在脚本中使用访问器来访问,例如:

1
2
3
4
5
{
    var a = argument0;    // 假设 argument0 传递的是一个数组
    a[@ 0] = 5;
    a[@ 1] = 6;
}

如上所示,[@ x] 就是数组的访问器了。由于这里是 GM8 的教程,所以不再详讲 GMS2 的访问器。

注意,这仅仅只是 GM 数组的特殊性,在绝大部分其他编程语言中并非如此


章节导航