前边我们讲了变量,打了一个形象的比喻,就是把变量比作吃饭的碗,量(值)就是饭。其实呢,数组跟变量是有直接关系的,变量是数组的基础,变量的是碗的话,数组就好像是一个多功能的饭盒,可以同时装好多种饭菜,神奇吧!!
来吧!然我们一起领略一下数组的神奇。常见的数组有三种,伪数组、数组、关联数组。
伪数组
伪数组应该是AutoHotkey中特有的概念,每个伪数组实际上只是一系列连续编号的变量或函数的集合,它们的每一个被视为伪数组的“元素”。AutoHotkey 内部不会以任何方式把这些元素链接在一起。任何接受 OutputVar
或能赋值的命令都可以用来创建伪数组。
最简单的例子是使用赋值运算符(:=)
,如下所示:
Array%j% := A_LoopField
通过在索引间使用自选分隔符可以创建多维伪数组,例如:
Array%j%_%k% := A_LoopReadLine
操作伪数组下面的例子演示了如何创建和访问伪数组,这里是从文本文件获取一系列的名称:
; 例1:写入数据到伪数组:
ArrayCount := 0
Loop, Read, C:\Test.txt ; 循环获取文件中的每行,一次一行。
{
ArrayCount += 1 ; 记录伪数组中的项目数,这里不记录后面获取不方便。
Array%ArrayCount% := A_LoopReadLine ; 把此行保存到伪数组中的下一个元素。
}
; 例2:从伪数组中读取:
Loop, %ArrayCount%
{
; 下一行使用 := 运算符获取伪数组元素:
element := Array%A_Index% ; A_Index 是内置变量。
MsgBox, % "索引号" . A_Index . "的元素的值为" . Array%A_Index%
}
看过这两个例子后,里面的操作中我们只是连续的操作变量罢了,与操作普通变量在性质上没有区别。唯一的区别是名称,这系列变量的名称一般后面部分为数字,比较典型的如 StringSplit、WinGet List
及 RegExMatch
这些命令创建的伪数组。还有些命令虽然会创建一系列变量,变量的名称却不是以数字结尾,如 GuiControlGet Pos、SysGet
等。
数组
简单数组创建数组:
Array := [Item1, Item2, ..., ItemN]
Array := Array(Item1, Item2, ..., ItemN)
获取项:
Value := Array[Index]
对项进行赋值:
Array[Index] := Value
插入一个或多个东东到给定序号位置:
Array.InsertAt(Index, Value, Value2, ...)
追加一个或多个东东:
Array.Push(Value, Value2, ...)
移除项:
RemovedValue := Array.RemoveAt(Index)
移除最后一项:
RemovedValue := Array.Pop()
如果数组不是空的, 那么 MinIndex
和MaxIndex/Length
分别返回数组中当前使用的最小和最大的索引. 由于最小的索引几乎总是 1, 所以 MaxIndex
经常返回项目数. 如果没有整数键, MaxIndex
返回空而 Length
返回 0 ,对数组内容进行依次循环可以通过索引或 For
循环实现。
例如:
array := ["one", "two", "three"]
; 从 1 到项目数进行重复:
Loop % array.Length()
MsgBox % array[A_Index]
; 枚举数组内容:
For index, value in array
MsgBox % "Item " index " is '" value "'"
关联数组
关联数组是一种数据结构,本质上是一种特殊的对象。操作关联数组自包含关联数组可以使用以下两种创建
<pre>Array := {KeyA: ValueA, KeyB: ValueB, ..., KeyZ: ValueZ}
Array := Object("KeyA", ValueA, "KeyB", ValueB, ..., "KeyZ", ValueZ)</pre>
例如:
; 创建数组后,初始为空:
Array := Object()
; 写入数据到数组:
Loop, Read, C:\Guest List.txt ; 依次获取文件中的每行文本。
{
Array.Insert(A_LoopReadLine) ; 添加到数组中。
}
; 从数组中读取数据,在一般情况下建议使用这种方式(即 for 循环):
for index, element in Array
{
MsgBox, % "索引号为" . index . "的元素的值为" . element
}
这个例子仅演示了对象提供的功能中很小的一部分,还可以可以设置、获取、插入、移除和枚举项目。除了数字,还可以把字符串和对象作为键使用。对象可以作为值存储到其他对象中并且可以作为函数参数或返回值传递。对象还可以用新功能进行扩展。
For 循环与 Loop 循环
对于这样特殊的关联数组,还可以把上面的 For 循环替换为 Loop 循环:
; 使用传统方式
Loop, % Array.MaxIndex()
{
; 使用“Loop”,索引必须是连续的数字,从 1 到数组中元素的个数(或者必须在循环中进行计算)。
MsgBox, % "索引号为" . A_Index . "的元素的值为" . Array[A_Index]
}
实际上不建议使用 Loop 对关联数组进行循环操作(很蹩脚),上面的例子中数组的索引从 1 开始且是连续的整数,所以可以使用 Loop 循环。然而,关联数组中的键可以为字符串、整数或对象,即使键为整数时还可能是稀疏分布的,例如{1:"a",1000:"b"}
,在这些一般情况下都无法使用 Loop 代替。
数组、伪数组与关联数组的之间的比较和应用
尽管 Insert() 和枚举数有它们的用途, 不过一些用户可能会发现使用它们比用更传统的方式容易些。下面的例子中把伪数组和关联数组的操作方式进行比较,其中注释中的为伪数组的操作方式:
; 与变量可直接使用不同,数组在使用前必须初始化:
Array := Object()
; Array%j% := A_LoopField
Array[j] := A_LoopField
; Array%j%_%k% := A_LoopReadLine
Array[j, k] := A_LoopReadLine
ArrayCount := 0
Loop, Read, C:\Guest List.txt
{
ArrayCount += 1
; Array%ArrayCount% := A_LoopReadLine
Array[ArrayCount] := A_LoopReadLine
}
Loop, %ArrayCount%
{
; element := Array%A_Index%
element := Array[A_Index]
; MsgBox % "Element number " . A_Index . " is " . Array%A_Index%
MsgBox, % "Element number " . A_Index . " is " . Array[A_Index]
}
这个是帮助中的例子,但实际意义不大,因为它是以伪数组的方式在伪数组的功能上进行的比较。例如使用 ArrayCount
变量保存数组元素个数,我们知道这对于关联数组是多余的,它的元素个数是由程序维护,可以直接使用 MaxIndex()
方法获取,而且获取值时无需通过元素个数,可直接 for
循环。帮助中提到这个比较会“让大家更容易从原来使用伪数组的习惯中过渡过来”,不过我个人认为这样做是否像用汉字标出英语单词的发音来学英语呢?
关联数组使用与简单数组非常相似的语法. 事实上, 在 v1.x 中它们是相同的东西. 然而, 把 []
视为简单线性数组有助于保持其作用清晰, 并且改善您脚本与 AutoHotkey 未来版本的兼容性, 未来版本中可能改变实现方式.
小结
目前而言,在支持创建伪数组的命令中还是需要伪数组(伪数组存在的必要性),在其他地方关联数组具有优势(关联数组的功能强大、使用方便),即关联数组尚无法完全取代伪数组(即使作为中间产物),尽管我推荐在一般情况下使用关联数组。