跳转到内容

ActionScript 2.0/数组简介

来自维基教科书,开放世界中的开放书籍
ActionScript 2.0 简介
基本数据类型 数组 影片剪辑


关键概念


  • 索引数组与关联数组
  • Array 构造函数和数组字面量
  • 嵌套数组
  • 数组连接
  • 向数组添加和删除元素
    • 弹出、推入、移位和不移位
    • 拼接
  • 通过切片提取数组元素
  • 数组和字符串之间的相互转换
  • 数组排序
    • 对数字和字符串进行排序
    • 自定义标准
    • 返回索引
    • 按字段排序
  • 反转数组

我们在控制语句章节中了解了数组。但是,我们没有学习如何操作它们。这是我们本章的重点。

Array 类的本质是什么?

[编辑 | 编辑源代码]

在计算机编程中,实际上存在两种类型的数组,索引数组关联数组(也称为哈希)。在索引数组中,每个数组元素都有一个索引,而在关联数组中,每个数组元素都有一个名称。在 ActionScript 中,Array 类生成索引数组,而 Object 类生成关联数组。

在关于控制语句的章节中,我们已经介绍了 Array 类构造函数在没有参数的情况下使用,我们将在本章中不再重复。在本章中,我们将介绍使用构造函数的另外两种方式,以及数组字面量。(是的,数组也有字面量!)

代码 结果
var dogs:Array = new Array(10);
dogs[0] = "Spot";
dogs[1] = "Lucky";
dogs[2] = "Max";
...
dogs[9] = "Rover";
trace(dogs[0]);
var fruits:Array = new Array("Apples", "Bananas", "Oranges", "Pears");
trace (fruits[2]);
var months:Array = ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"];
trace(months[9]);

斑点
橙子
O

在第一个例子中,我们通过指定长度来初始化 dogs 数组,然后为每个元素分配一个字符串值。在第二个例子中,我们将值直接放入构造函数中。[1] 在第三个例子中,我们使用了数组字面量。数组字面量总是用一对方括号括起来,每个元素之间用逗号隔开,例如:[1,1,3,5,8,13,21,34]。

什么是多维数组?

[编辑 | 编辑源代码]

多维数组嵌套数组是指包含数组作为元素的数组。它可以以表格形式表示数据。例如

代码 结果
var multiplicationTables:Array = new Array(10);
for(var i:Number = 0; i < multiplicationTables.length; i++){
     multiplicationTables[i] = new Array(10);
     for(var j:Number = 0; j < multiplicationTables[i].length; j++){
          multiplicationTables[i][j] = i * j;
     }
     trace(multiplicationTables[i]);
}

0,0,0,0,0,0,0,0,0,0
0,1,2,3,4,5,6,7,8,9
0,2,4,6,8,10,12,14,16,18
0,3,6,9,12,15,18,21,24,27
0,4,8,12,16,20,24,28,32,36
0,5,10,15,20,25,30,35,40,45
0,6,12,18,24,30,36,42,48,54
0,7,14,21,28,35,42,49,56,63
0,8,16,24,32,40,48,56,64,72
0,9,18,27,36,45,54,63,72,81

一旦我们学习了动态影片剪辑和文本字段的创建以及绘图 API,我们就可以实际将此数组呈现为表格。

幸运的是,我们可以使用数组字面量来创建嵌套数组的快捷方式

代码 结果
var someNestedArray:Array = new Array([1, 3, 5, 7, 9], [1, 1, 2, 3, 5], [2, 4, 6, 8, 10]);
trace(someNestedArray[0]);
trace(someNestedArray[1]);
trace(someNestedArray[2]);
trace(someNestedArray);

1,3,5,7,9
1,1,2,3,5
2,4,6,8,10
1,3,5,7,9,1,1,2,3,5,2,4,6,8,10

请注意,在最后一个跟踪中,多维数组在转换为字符串并跟踪时被“展平”了。我们可以使用 split 方法创建一个完全展平的数组

代码 结果
var someNestedArray:Array = new Array([1, 3, 5, 7, 9], [1, 1, 2, 3, 5], [2, 4, 6, 8, 10]);
var someFlattenedArray:Array = String(someNestedArray).split(",");
trace(someFlattenedArray.length);

15

由于长度为 15,因此有 15 个元素,而不是三个元素,每个元素包含五个元素。

如何将两个数组连接在一起?

[编辑 | 编辑源代码]

连接两个数组是一个相当简单的操作。它需要使用 array1.concat(array2, array3...) 方法。此方法不会展平嵌套数组。以下代码将两个数组合并在一起

代码 结果
var array1:Array = new Array("a", "b", "c");
var array2:Array = new Array("d", 5, "f");
var array3:Array = array1.concat(array2);
trace(array3);

a,b,c,d,5,f

concat() 不应与稍后介绍的 join() 混淆。

如何向数组添加和删除元素?

[编辑 | 编辑源代码]

有几种方法可以向数组添加和删除元素

  • array.pop()
  • array.push(object1, object2...)
  • array.shift()
  • array.unshift(object1, object2...)
  • array.splice(第一个元素的索引, [元素数量,] [object1, object2...])

这开始变得有点吓人,所以让我们逐个介绍它们。

弹出和推入

[编辑 | 编辑源代码]

pop() 从数组中删除最后一个元素,然后返回弹出的元素。此示例将展示“pop”方法的工作原理

代码 结果
var myArray = new Array(1, 2, 3, 4);
var popped:Number = myArray.pop();
trace(popped);
trace(myArray);

4
1,2,3

首先,我们声明 'myArray' 数组,其中包含四个元素:1、2、3 和 4。接下来,我们声明 'popped' 变量。然后,我们弹出 'myArray' 并将最后一个元素分配给 'popped' 变量。然后,我们跟踪 popped 以查看弹出的元素,然后跟踪新数组。

push() 向数组末尾添加一个或多个元素,然后返回新数组的长度

代码 结果
var myArray = new Array(1, 2);
var newLength:Number = myArray.push(3, 4);
trace(newLength);
trace(myArray);

4
1,2,3,4

我们首先声明 'myArray' 数组,其中包含两个元素,1 和 2。第二行将值 3 和 4 推入 myArray(返回新长度),声明 'newLength' 变量,然后将新长度分配给 newLength。

移位和不移位

[编辑 | 编辑源代码]

移位和不移位类似于弹出和推入,但元素是从数组的开头添加和删除,而不是结尾。

以下是如何移位的示例

代码 结果
var myArray = new Array(1, 2, 3, 4);
var shifted:Number = myArray.shift();
trace(shifted);
trace(myArray);

1
2,3,4

首先,我们声明 'myArray' 数组,其中包含四个元素:1、2、3 和 4。接下来,我们声明 'shifted' 变量。然后,我们移位 'myArray' 并将第一个元素分配给 'shifted' 变量。然后,我们跟踪 shifted 以查看移位的元素,然后跟踪新数组。

以下是如何不移位的示例

代码 结果
var myArray = new Array(3, 4);
var newLength:Number = myArray.unshift(1, 2);
trace(newLength);
trace(myArray);

4
1,2,3,4

我们首先声明 'myArray' 数组,其中包含两个元素,3 和 4。第二行将值 1 和 2 不移位到 myArray(返回新长度),声明 'newLength' 变量,然后将新长度分配给 newLength。

拼接数组比推入、弹出、移位和不移位更灵活,更复杂。(切片、拆分、拼接、移位……糊涂了吗?)拼接是同时从数组中删除和添加元素的过程。splice() 方法向数组添加元素并从数组中删除元素,然后返回删除的元素。

让我们再看看参数

array.splice(index of the first element, [number of elements], [new element 1, new element 2...])

如您所见,只有第一个参数是必需的。第一个参数指示您要开始执行删除和添加操作的位置。它可以是普通索引(0、1、2、3……),也可以是负数(-1 作为最后一个元素,-2、-3、-4……)。如果这是您包含的唯一参数,则将从该参数开始删除所有元素

代码 结果
var myArray:Array = [1, 2, 3, 4];
myArray.splice(2);
trace(myArray);

1,2

第二个参数是要删除的元素数量。让我们修改上面的示例,只删除 3

代码 结果
var myArray:Array = [1, 2, 3, 4];
myArray.splice(2, 1);
trace(myArray);

1,2,4

其余参数是要添加到数组中的元素,以替换删除的元素。

代码 结果
var myArray:Array = [1, 2, 3, 4];
myArray.splice(2, 1, 6, 7, 8);
trace(myArray);

1,2,6,7,8,4

我们也可以将第二个参数设置为 0,这样我们就可以添加元素,而不会删除任何元素

代码 结果
var myArray:Array = [1, 2, 3, 4];
myArray.splice(2, 0, 6, 7, 8);
trace(myArray);

1,2,6,7,8,3,4

如何从数组中提取元素?

[编辑 | 编辑源代码]

slice() 返回数组中的某些元素。以下是 slice() 的参数

array.slice([index of the first element], [index of the last element]);

两个参数都是可选的。如果两个元素都为空,则复制整个数组

代码 结果
var myArray:Array = [1, 2, 3, 4];

var secondArray:Array = myArray.slice();
secondArray.pop();
trace(secondArray);
trace(myArray);

var thirdArray:Array = myArray;
thirdArray.pop();
trace(thirdArray);
trace(myArray);

1,2,3
1,2,3,4
1,2,3
1,2,3

再次强调,整个数组是复制,而不是链接。在上面的例子中,改变 secondArray 不会改变原始的 myArray,因为 myArray 被复制到了 secondArray。然而,改变 thirdArray 会改变原始的 myArray,因为 thirdArray 只包含指向 myArray 的链接,而不是 myArray 的副本。

让我们再试一次,这次使用第一个参数

代码 结果
var myArray:Array = [1, 2, 3, 4, 5];
var slicedArray:Array = myArray.slice(2);
trace(slicedArray);

3,4,5

只从元素 2 开始的所有内容都被复制到 slicedArray。现在让我们第三次尝试,使用两个参数

代码 结果
var myArray:Array = [1, 2, 3, 4, 5];
var slicedArray:Array = myArray.slice(2, 3);
trace(slicedArray);

3,4

只从元素 2 到元素 3 的元素被复制到 slicedArray。

连接和分割是什么?

[编辑 | 编辑源代码]

有时,我们希望将一个字符串分割成多个由分隔符(一个符号,指示我们应该在何处分割字符串)分隔的子字符串,然后将每个子字符串放入一个数组中。在这种情况下,我们需要使用 split 方法

代码 结果
trace("Foo + Bar + Baz + Qux".split(" + "));
trace("Foo + Bar + Baz + Qux".split(" + ", 3));
trace("Foobar".split(""));

Foo,Bar,Baz,Qux
Foo,Bar,Baz
F,o,o,b,a,r

在第一个跟踪中,我们使用 " + " 作为分隔符来创建一个包含元素 Foo、Bar、Baz 和 Qux 的数组。在第二个跟踪中,我们将子字符串的数量限制为 3。在第三个跟踪中,分隔符是一个空字符串,因此每个子字符串都包含一个字符。

连接数组是分割字符串的逆运算。它使用数组元素和某个分隔符(第一个也是唯一的参数)创建一个新的字符串

代码 结果
trace(["Foo", "Bar", "Baz", "Qux"].join(" + "));
trace(["F", "o", "o", "b", "a", "r"].join(""));
trace(["Foo", "Bar", "Baz", "Qux"].join());

Foo + Bar + Baz + Qux
Foobar
Foo,Bar,Baz,Qux

在第一个跟踪中,我们使用 " + " 将 Foo、Bar、Baz 和 Qux 连接成一个字符串。在第二个跟踪中,我们得到了单词 "Foobar",因为我们使用空字符串作为分隔符。在第三个跟踪中,我们产生了与 trace(String(["Foo", "Bar", "Baz", "Qux"].join())); 相同的效果,因为我们没有指定任何分隔符,并且使用了默认的逗号。

如何排序数组?

[编辑 | 编辑源代码]

在 ActionScript 中,排序是一个非常复杂的主题。在本节中,我们将尝试做到这一点!

如何使用 sort() 排序?

[编辑 | 编辑源代码]

sort() 函数可以使用一个条件进行排序。

如何使用 Unicode 代码点排序字符串?

[编辑 | 编辑源代码]

排序一堆字符串的最简单方法是使用 Unicode 代码点。(我们早在关于运算符的章节中讨论过 Unicode 代码点。)这不需要任何参数

代码 结果
var myArray:Array = ["spot", "lucky", "Rover", "Max"];
myArray.sort();
trace(myArray);
myArray = [1, 300, 2, 4, 6];
myArray.sort();
trace(myArray);

Max,Rover,lucky,spot
1,2,300,4,6

请注意,Lucky 和 Max 在 rover 和 spot 之前,因为大写字母在 Unicode 中排在小写字母之前。还要注意,数字也是根据它们的 Unicode 代码点排序,而不是它们的幅度或值。

如果我们希望排序忽略大小写,我们可以将一个 Array 类常量 Array.CASEINSENSITIVE 传递给 sort() 函数

代码 结果
var myArray:Array = ["spot", "lucky", "Rover", "Max"];
myArray.sort(Array.CASEINSENSITIVE);
trace(myArray);

lucky,Max,Rover,spot

Array.CASEINSENSITIVE 可以用数字 1 替换。

如何排序数字?

[编辑 | 编辑源代码]

正如我们上面所看到的,sort() 默认情况下将元素作为字符串排序。要排序数字,我们需要将另一个 Array 类常量 Array.NUMERIC 传递给 sort() 函数

代码 结果
var myArray:Array = [1, 300, -Infinity, 2, 4, 4.3e-3, Infinity, -3, 6, 0x2A];
myArray.sort(Array.NUMERIC);
trace(myArray);

-Infinity,-3,0.0043,1,2,4,6,42,300,Infinity

请注意,负无穷大总是最小值,无穷大总是最大值。Array.NUMERIC 可以用数字 16 替换。


如何使用自己的标准排序?

[编辑 | 编辑源代码]

有时,使用 Unicode 代码点还不够。在这种情况下,我们必须创建自己的函数。sort() 函数的第一个参数是一个包含两个参数的函数,它们决定两个值中的哪个应该排在前面。如果第一个参数排在第二个参数前面,该函数应该返回 1;如果第二个参数排在第一个参数前面,则返回 -1;如果无关紧要,则返回 0。如果听起来很复杂,请查看此示例

代码 结果
var myArray:Array = ["a good dog called spot", "a canine called lucky",
                     "a cute mammal called Rover", "an adorable animal called Max"];
function whichComesFirst(a:String, b:String):Number{
     var dog1:String = a.substring(a.lastIndexOf(" ")+1).toUpperCase();
     var dog2:String = b.substring(b.lastIndexOf(" ")+1).toUpperCase();
     if(dog1 > dog2){
          return 1;
     } else if(dog1 < dog2) {
          return -1;
     } else {
          return 0;
     }
}
myArray.sort(whichComesFirst);
trace(myArray);

a canine called lucky,an adorable animal called Max,a cute mammal called Rover,a good dog called spot

在 whichComesFirst 函数中,我们首先从字符串中提取了最后一个单词。然后,我们在比较之前将所有内容转换为大写。这样,大小写就被忽略了。此函数按照真正的字母顺序对数组进行排序,类似于我们使用 Array.CASEINSENSITIVE 达到的效果。

如何以降序排序?

[编辑 | 编辑源代码]

要以降序排序,我们将值 Array.DESCENDING 传递给第一个参数

代码 结果
var myArray:Array = ["spot", "lucky", "Rover", "Max"];
myArray.sort(Array.DESCENDING);
trace(myArray);

spot,lucky,Rover,Max

这也适用于自定义标准(请注意,比较函数是第一个参数,数组常量是第二个参数)。

代码 结果
var myArray:Array = ["a good dog called spot", "a canine called lucky",
                     "a cute mammal called Rover", "an adorable animal called Max"];
function whichComesFirst(a:String, b:String):Number{
     var dog1:String = a.substring(a.lastIndexOf(" ")+1).toUpperCase();
     var dog2:String = b.substring(b.lastIndexOf(" ")+1).toUpperCase();
     if(dog1 > dog2){
          return 1;
     } else if(dog1 < dog2) {
          return -1;
     } else {
          return 0;
     }
}
myArray.sort(whichComesFirst, Array.DESCENDING);
trace(myArray);

a good dog called spot,a cute mammal called Rover,an adorable animal called Max,a canine called lucky

如果我们已经有了另一个数组常量作为参数,我们应该使用按位或运算符 (|)[2] 来分离两个数组常量。

代码 结果
var myArray:Array = [1, 300, -Infinity, 2, 4, 4.3e-3, Infinity, -3, 6, 0x2A];
myArray.sort(Array.NUMERIC | Array.DESCENDING);
trace(myArray);

Infinity,300,42,6,4,2,1,0.0043,-3,-Infinity

如果我不希望修改原始数组怎么办?

[编辑 | 编辑源代码]

数组常量 Array.RETURNINDEXEDARRAY 将使 sort() 返回一个包含每个数字的新索引的数组。例如,如果原始数组是 [3, -1, 4],则 sort() 将返回 [1, 0, 2],其中 3 是排序数组中的第二个数字(索引 1),-1 是第一个数字(索引 0),4 是第三个数字(索引 2)。

代码 结果
var myArray:Array = [1, -3, 5, 7, 3];
var myIndices:Array = myArray.sort(Array.NUMERIC | Array.RETURNINDEXEDARRAY);
trace(myIndices);
var mySortedArray:Array = new Array();
for(i = 0; i < myIndices.length; i++){
	mySortedArray[i] = myArray[myIndices[i]];
}
trace(mySortedArray);

1,0,4,2,3
-3,1,7,3,5

请注意,数据库中的术语略有不同。在数据库中,myIndices 被称为索引,而我们的数组索引被称为索引键。在这本书中,为了避免混淆,我们将避免使用这些术语。

如果我不希望重复项怎么办?

[编辑 | 编辑源代码]

如果您使用 Array.UNIQUESORT 常量,如果发现两个或多个元素等效,Flash 将返回 0 而不是修改数组。我们将在第四节中使用此常量,届时我们将学习异常处理。

如何使用 sortOn() 排序数组?

[编辑 | 编辑源代码]

有时,我们希望对数组中的哈希或对象进行排序,而使用 sort() 以及自定义的排序条件则显得过于繁琐。这时,sortOn() 就可以派上用场。

sortOn 将数组视为数据库中的表格。在数据库中,表格 是一个实体或记录的集合,这些实体或记录具有相同的记录结构。每个实体都有若干字段,每个字段都存储记录的特定属性。有时,我们需要使用查询 以特定的方式对字段进行排序。排序键 是用于排序的字段。主要排序键 首先用于对数据进行排序。当两个或多个数据在某个字段中具有相同的属性时,将使用次要排序键 对这些数据进行排序。如果仍然存在等效值,则使用三级排序键,依此类推。

假设我们有一个包含关于多本书籍数据的表格。我们想要按书名降序排列书籍标题。如果存在两本书具有相同的书名,则按作者姓氏升序排列,然后按作者名升序排列,最后按出版年份降序排列。在 SQL 中,执行此操作的语法为

SELECT *
FROM Books
ORDER BY Title DESC, Author_last ASC, Author_first ASC, Pub_year DESC;

即使您对 SQL 一无所知,您也可能理解其中一部分代码。不幸的是,对于 ActionScript 而言,情况要复杂得多。让我们尝试将这段 SQL 代码改写为 ActionScript 代码

代码 结果
var books:Array = new Array({title:"The Adventures of Teddy Bear", last:"Bloggs", first:"Joe", year:1996},
                            {title:"Journey to the West", last:"Wu", first:"Cheng'en", year:1542},
                            {title:"The Adventures of Teddy Bear", last:"Bloggs", first:"Fred", year:2012});
books.sortOn(["title", "last", "first", "year"],
             [Array.CASEINSENSITIVE | Array.DESCENDING, Array.CASEINSENSITIVE,
              Array.CASEINSENSITIVE, Array.NUMERIC | Array.DESCENDING]);
for(var i:String in books){
	trace(books[i].title + " by " + books[i].first + " "
        + books[i].last + " published in " + books[i].year);
}

西游记,吴承恩著,1542 年出版
泰迪熊历险记,弗雷德·布洛格斯著,2012 年出版
泰迪熊历险记,乔·布洛格斯著,1996 年出版

看起来很复杂!让我们逐一分解。

  • 首先,我们使用三个对象字面量创建了一个新的数组。每个对象都有一个标题、一个姓氏、一个名字和一个年份。请注意,此处每个对象都是一个关联数组。
  • 接下来,我们使用 sortOn。sortOn 的第一个参数是一个数组,该数组包含用作排序键的属性名称。第 0 个元素 title 是主要排序键,第 1 个元素 last 是次要排序键,依此类推。
  • 第二个参数是一个数组,该数组包含用于每个排序的数组常量。在本例中,我们希望对字符串字段使用 Array.CASEINSENSITIVE,对出版年份使用 Array.NUMERIC,对作者和出版年份使用 Array.DESCENDING。还使用了按位 OR 运算符。
  • 最后,我们从第 0 个元素到第 2 个元素跟踪了新的数组。

分解之后,它并不难,对吧?我们也可以使用它对 MovieClips 和其他类的实例进行排序。这种看似无用的技术在您需要呈现大量元素时非常有用。例如,如果您要制作一个带有关卡编辑器的游戏,则需要一种方法来显示关卡。玩家可以按出版日期、评分、难度等对关卡进行排序。

如何反转数组?

[编辑 | 编辑源代码]

reverse() 是反转数组的简便快捷方法。

代码 结果
var myArray:Array = [1, 300, -Infinity, 2, 4, 4.3e-3, Infinity, -3, 6, 0x2A];
myArray.sort(Array.NUMERIC | Array.DESCENDING);
trace(myArray);
myArray.reverse();
trace(myArray);

Infinity,300,42,6,4,2,1,0.0043,-3,-Infinity
-Infinity,-3,0.0043,1,2,4,6,42,300,Infinity

我们已经完成了第二部分中的两章,到目前为止,我们只是在处理数据,而没有在屏幕上进行任何实际的输出。这可能让您不太满意,因此让我们继续讨论 Flash 应用程序的核心:MovieClip。

  1. 同样,由于存在全局函数 Array(),因此如果您不想,则不必在 Array() 之前添加 'new'。
  2. 按位 OR 运算符超出了本书的范围,因此我们不会讨论它的具体工作原理。
华夏公益教科书