# 表（ table ）

表（ table ）是 aardio 中唯一的复合数据类型。除了非复合的基础数据类型以外，aardio 中几乎所有的复合对象都是表，即使是变量的命名空间也是表。

表的本质是一个集合(collection)，可以用于容纳其他的数据成员，并且表也可以嵌套的包含其他的表（table），在aardio里表几乎可以容纳一切其他对象。

如果我们把字符串、buffer、数值、函数、指针.....这些基础数据类型比喻为盘子中的菜，那么表（table）这样的复合数据类型就是装菜的盘子，如果没有了盘子，所以就没有办法愉快的吃菜了。

aardio 中的表可以包含不定个数的成员，每个成员都由一个键值对组成（键用来指定成员的名称，值用来指定成员的值）。“键”可以是任何除 null 以外的数据类型。“值”也可以是任意数据类型，当值为 null 时表示删除该成员。table 也允许嵌套，可以在 table 元素中包含 table 。

通常把“键”放在索引操作符`[]`中来索引一个元素的值，这时候键又称为“下标”或“索引”。例如 `tab["键"]` 或 tab[1]，`[]`则被称为下标操作符。也可以把一个符合标识符命名规则的键名放在成员操作符`.`后面,例如 `tab.key` ， `tab.key2` 。

## 基础知识

### 表与数组

表包含的成员类型主要分为哈希表与有序数组，表可以同时包含这两种类型的成员。

1. 哈希表（无序集合）

    哈希表用于包含不定个数的键值对成员，使用哈希算法建议键与值之间的映射以实现高效的元素查询。

    哈希表描述的数据结构通常也可以称为字典(dictionaries)、映射(map)、关联数组(associative arrays)、对象(object)...... 等等。当然这些数据结构在不同语言中存在不同实现，也可能不会用到哈希算法，但描述的数据结构相似。

    哈希表使用哈希算法存取，不会按其他特定顺序排序（在代码中添加成员的顺序将被忽略）。

    在遍历哈希表时的顺序并不一定会保持代码中书写、添加表成员的顺序，因此我们把这种集合称为无序集合。哈希表的优势是查找速度非常快，即使表包含的成员非常多，仍然可以快速地访问指定的成员。  
    
    下面是一个创建哈希表的例子：
    
    ```aardio
    var tab = {
        a = 123;
        str = "字符串";
        [123] = "不符合变量命名规则的键名应放在下标内。";
        ["键 名"] = "不符合变量命名规则的键名应放在下标内。";
        键名 = {
            test = "表也可以包含表";
        }
    }
    ```  

    用 `{}` 表示创建一个表，表成员写在`{}` 内部， 使用

    aardio 允许用 `,` 号代替  `;` 号分隔表成员 ，并且允许用 `:` 替代 `=` 分隔键值对，也允许在表中用引号(双引号、单引号、反引号 )包含的字符串表示键名。因此可以使用类 JSON 的语法定义表对象，例如：  
  
    ```aardio
    var tag ={"name1":123,"name2":456}
    ``` 
  

2. 数组（有序集合） <a id="array" href="#array">&#x23;</a>

    如果表中不定个数的成员的“键”是从 1 开始、有序、连续的数值，那么这些成员构成一个有序的 <a id="dense-array" href="#dense-array">稠密数组（ dense array ）</a>。 在 aardio 中未加特别说明时“数组”都是指这种稠密数组。

    创建数组的示例：

    ```aardio
    var array = { 
        [1] = 123;
        [2] = "数组的值可以是任何其他对象"; 
        [3] = { "也可以嵌套包含其他表或数组"}
    } 
    ```
    上面我们将数值键放在下标`[]`中，用分号`;`分隔数组成员（也可以使用逗号`,`分隔数组成员），用等号`=`分隔键值对。

    表构造器中稠密数组的数值键可以省略不写，直接用出现的位置顺序作为数值索引，示例：

    ```aardio
    var array = { 
        123,456,789,"其他数组值"
    } 
    ```

    表对象可以同时包含稠密数组成员与其他类型的键值对，即使不包含数组成员，我们也可以将表作为空数组处理。

    如果用 `[]` 包围数组则可以创建纯数组对象，例如 `[1,2,3]`。纯数组仍然是一个表，但纯数组构造器 `[]` 内不能再用 `[]` 包围一个表达式作为元素的`键`。详细说明请参考 [纯数组](#pure-array-table)

    > 如果目的只是创建稠密数组，则应当总是使用 `[]` 构造纯数组，用 `{}` 去创建一个单纯的稠密数组是没有必要的。

    稠密数组可以使用 for 循环（ 或者 table.eachIndex ）有序地遍历成员，示例:

    ```aardio
    import console; 

    var array = [
        123,
        "数组的值可以是任何其他对象",
        [ "也可以嵌套包含其他表或数组"]
    ]

    //遍历数组成员, 用 #array 取数组长度
    for(i=1;#array;1){
        console.dump(i,array[ i ]);
    }

    //使用 table.eachIndex 也可以遍历数组
    for i,v in table.eachIndex(array){
        console.dump(i,v);
    }

    console.pause();
    ```

## 构造表 <a id="initializer" href="#initializer">&#x23;</a>

表的构造器是用一对大括号 `{}` 包含零个或多个用分号`;`分隔的元素（键值对或数组成员），使用等号`=`分隔键值对。

```aardio
import console; 
 
//空表
var tab = {}

//哈希表、对象
var object = {
	key1 = "字符串";
	key2 = 123;
    [123] = "不符合变量命名规则的键名应放在下标内。";
    ["键 名"] = "不符合变量命名规则的键名应放在下标内。";
}

//稠密数组成员可以省略数值索引，以出现位置作为索引（起始索引为 1 ）
var array = {
	123;456;789;"其他值"
}


console.pause(true);
```  

表可以包含不同类型的数据，可以同时包含键值对与数组成员。

如果表的键名不符合标识符命名规则或者不是字符串，则必须写在下标内，例如：

```aardio
var object = {
    [123] = "不符合变量命名规则的键名应放在下标内。";
    ["键 名"] = "不符合变量命名规则的键名应放在下标内。";
}
```a

aardio 中的表允许用 `,` 号代替  `;` 号分隔表成员 ，并且允许用 `:` 替代 `=` 分隔键值对，也允许在表中用引号(双引号、单引号、反引号 )包含的字符串字面量表示键名。因此可以使用类 JSON 的语法定义表对象，例如：  

```aardio
var tag ={"name1":123,"name2":456}
``` 

表中如果直接用数值字面量表示索引，则后面必须紧跟一个冒号（不能用等号替代）, 示例：

```aardio
var tag ={999:"稀疏数组成员";1;2;3}
``` 

表构造器 `{}` 内表示键值对的不同方法总结：

- 密数组成员可以省略数值索引，以出现位置作为索引（起始索引为 1 ）。
例如 `{1;2;3}` ，可用分号或逗号分隔数组成员。
- 名值对方式，例如 `{name="value";name2="value2"}`, 可使用 `:` 号代替 `=` 号分隔键值对。
- 用字符串字面量表示键名，例如 `{"name":123;"name2":789}`, 也可以使用 `:` 号或 `=` 号分隔键值对。
- 用数值字面量加一个冒号表示数值索引，例如 `{1;2;3;999:"稀疏数组成员"}`, 这种方式不能用 `=` 号分隔键值对。
- 将任意表达式放在下标操作符 `[]` 作为键，例如：`{ ["name"]="value";["name"]="value2"}`。

如果表的成员是一个函数，且函数体是一个语法块，则可以省略表分隔符，例如：

```aardio
var object = {
	func1 = function(){}
	func2 = function(){}
}
```

不能在表构造器后直接使用成员操作符或下标，例如 `{}.member` 这样写是错的。

## 纯数组 <a id="pure-array-table" href="#pure-array-table">&#x23;</a>

使用 `[]` 构造纯数组示例：

```aardio
var array = [
    123,456,789,"其他数组值" //可直接包含省略数值索引的稠密数组成员
]
``` 

纯数组（ pure-array ） 是一种特殊的表对象（pure-array table ） 。
纯数组的数据类型仍然是表（ table ）, 各种特性与表相同，同样可以包含稠密数组成员、稀疏数组成员、以及其他类型的键值对成员。

纯数组与普通表的主要区别在于构造方式以及序列化行为。

用 `[]` 作为数组构造器时，内部的语法与 `{}` 构造器基本相同，主要区别是：

- `[]` 构造器内部将 `[]` 包围的成员也识别为纯数组，但  `{}` 构造器内部  `[]` 包围的表达式表示元素的 `键`。
    * 纯数组正确写法： `[ 1，2，3，["嵌套数组"，4，5，6] ]` ，纯数组错误写法： `[ 1，2，3，["键"]="值"]` , 在 `[]` 构造器内 `[]` 包围的成员不是`键`。
    * 表的正确写法： `{ 1，2，3，["键"]=["数组值"] }`，表的错误写法: `{ 1，2，3，["嵌套数组"，4，5，6] }` , 在 `{}` 构造器内 `[]` 包围的成员不是数组。
- `[]` 构造器内部允许包含名值对，但不允许包含原生静态类型成员，也就是说 `[]` 不能用于构造结构体（但结构体中的原生数组字段值可以是纯数组）。

纯数组虽然也是表，也能包含非稠密数组类型的成员，但因为纯数组里不能用 `[]` 包围一个表达式作为成员的键，可以直接写在  `[]` 构造器内的是`键`的类型是有限的。纯数组构造器内也可以包含下列成员：

- 名值对：键名是字符串，键名可选用引号包围，键值分隔符可选用 `:` 号或 `=` 号。

    例如 `["name":123,"name2":789]` ，一般用  `:` 分隔即可，兼容 `=` 号只是为了用着宽松些

- 稀疏数组成员: 索引（键）是数值，只能用 `:` 号分隔键值。

    例如 `[1,2,3,999:"稀疏数组成员"]`，这里为什么不能用 `=` 分隔呢？因为写成 `[999=value]` 这样就不是很好。

> 纯数组的主要用途是表示稠密数组，也就是  `[1,2,3]` 这种简单用法。表示键值对集合用 `{name=value,name2=value}` 会更好。

纯数组的序列化行为与普通表也略有不同：

- 无法作为标志符的字符串键用字符串表示，不用 `[]` 包围`键`，而普通表则用 `[]` 包围字符串`键`。
- 稀疏数组的数值键使用 `:` 号分隔键值对，，不用 `[]` 包围`键`，而普通表则用 `[]` 包围稀疏数组的数值`键`。
- 序列化纯数组时总是用 `:` 号分隔非稠密数组成员的键与值，并使用逗号分隔所有元素。而普通表在序列化时使用 `=` 号分隔键值对，并使用分号分隔所有元素。

其他创建纯数组的方式：
- 使用标准库函数 table.array 创建纯数组
- 使用 table.range 函数返数值范围返回纯数组。
- 使用 table.splice,table.slice,table.unique 等用于操作并构造稠密数组的标准库函数会返回纯数组。
- 使用 com.SafeArray 可创建 COM 兼容的纯数组。
- COM 对象返回的数组都是纯数组。
- 使用结构体自内存转换并创建的 aardio 数组都是纯数组。

table.isArray 函数用于检查参数是否纯数组 ，table.isArrayLike 函数的检测条件则更宽松，参数是纯数组或包含稠密数组成员、或者使用元属性声明为数组、来自外部接口可识别处理为稠密数组的表都会返回 true 。

使用 JSON.stringify 序列化纯数组，即使纯数组为空也会返回 `[]`。
而其他普通表对象，则需要使用 table.isArrayLike 判断如果是类稠密数组对象则序列化为 JSON 数组。

> 如果目的只是创建稠密数组，则应当总是使用 `[]` 构造纯数组，用 `{}` 去创建一个单纯的稠密数组是通常是没有必要的。

### 稀疏数组 <a id="sparse-array" href="#sparse-array">&#x23;</a>

稀疏数组可以理解为无序集合（哈希表）的一个特例。

如果表中包含的成员使用了数值作为键，但是多个成员的键并不是从1开始的连续数值 - 则构成稀疏数组。 

如果表中包含了稀疏数组 - 也就是说成员的数值键（索引）包含不连续的、中断的数值，那么不应当作为有序数组使用。 

在 aardio 一般我们提到的数组 - 如果未加特别说明则是特指稠密数组（不包含稀疏数组）。aardio 中几乎所有针对数组操作的函数或操作符 - 如果未加特别说明都要求参数是有序的稠密数组。

至于稀疏数组我们将其作为一般的哈希表操作就可以了，不要用普通的数组函数去操作稀疏数组。
  
下面的数组就包含了 null 值，属于数值键（索引）不连续的稀疏数组：  

```aardio
var sparseArray = { "值：1", null, "值：2", "值：4", null, null }
```  

null 值就是没有值，所以在表的尾部写 null 值是无意义的，正确写法如下：  

```aardio
var sparseArray = { "值：1", null, "值：2", "值：4" }
```  

表中间的 null 值仅仅是占位作用，因为 null 仍然是表示不存在的值，以上的表等价写法如下：  

```aardio
var sparseArray = {
    [1] = "值：1";
    [2] = "值：2";
    [4] = "值：4";    
}
```  

这种稀疏数组不应当作为有序的稠密数组使用，也不应当使用 `#` 操作符去获取稀疏数组的长度。 
  
稀疏数组可以用 table.range() 获取最小索引、最大索引。  
也可以使用 table.eachArgs() 遍历稀疏数组。  
  
示例： <a id="eachArgs" href="#eachArgs">&#x23;</a>

  
```aardio
import console.int;
var sparseArray = { "值：1", null, "值：2", "值：4" }

//获取稀疏数组长度
var min,max = table.range(sparseArray);

//遍历稀疏数组（可用于遍历函数的不定参数）
for i,v in table.eachArgs(sparseArray) {
    console.log(i,v)
    
}
```

参考：[范例 - 含 null 数组](../../../example/aardio/Array/sparse-array.html)

### 纯数组、类数组、伪数组 <a id="array-type" href="#array-type">&#x23;</a>

1. 纯数组（pure array）

使用 `[]` 操作符构造的数组称为`纯数组`，是 aardio 中专用于操作稠密数组的类型。

- 纯纯数组即使为空，传入 table.isArray 或  table.isArrayLike 都会返回 true 。
- aardio 中所有内置函数与标准库函数如果返回新的稠密数组，在没有特殊原因或特殊说明时返回的一定是纯数组类型。

2. 类数组（array-like table） <a id="array-like" href="#array-like">💡</a>

如果对象是一个`纯数组`，或者是包含稠密数组成员的`表`则统称为 `类数组`。
 
table.isArrayLike 函数用于检测对象是否`类数组`，检测步骤如下：

- 如果对象不是表则返回 false 。
- 如果对象是纯数组，则返回 true （即使数组为空）
- 用 `#` 操作符获取表中稠密数组元素个数得到非零值则返回 true 。
- 以上都不符合则返回 false 。

类数组适合使用基于数值范围的 for 循环直接遍历元素，也可以用  `#` 操作符直接获取稠密数组元素个数。

```aardio
if( table.isArrayLike(tab) ){
	print("tab 包含稠密数组成员");
	
	//循环输出所有稠密数组成员
	for(i=1;#tab;1){
		print(tab[i]);
	} 
}
```

2. 伪数组（pseudo-array）<a id="pseudo-array" href="#pseudo-array">💡</a>


可以使用 table.type 函数判定表对象是否`伪数组`，

`table.type(tab)` 用于检测参数指定的表对象的首选操作类型（preferred type）。  
如果参数 @tab 不是表（table）则返回 `null`，否则返回字符串 `"array"` 或者 `"object"`。

如果 table.type 函数返回 `"array"` 则表明表对象期适合作为`伪数组`使用 table.len, table.eachIndex` 等函数操作。
table.parseValue, table.strip, JSON.stringify 等函数会使用 table.type 判定是否需要将传入对象转换为数组。

table.type 的检测顺序为：
1. 纯数组总是返回 "array"。
2. _type 元属性如果明确指定了 "array" 或`"object" 则直接返回。
3. length 元属性或元方法返回非 null 值则返回"array"，如果指定了 length 元方法但调用元方法返回 null 值返回"object"。
4. 指定了 _keys 或 _defined 等预定义名值对的元属性返回 "object"。
5. 如果使用 `#` 操作符取对象的稠密数组元素个数返回非零值则返回 "array"。
6. 如果对象拥有元素，并且 length 或 Length 字段用数值指定了数组长度则返回 "array"。
7. 以上都不符合时默认返回 "object" 。

🅰 示例 1：

```aardio
var arr = ["a","b",length:3]

for i,v in table.eachIndex(arr){
	print(i,arr[i]);	
}
```

上面的代码控制台输出了：

```txt
1   a
2   b
3   null
```

可以看到 table.eachIndex 或 table.len 可以识别自定义的数组长度（即使对象是一个真正的纯数组）。

🅰 示例 2：

```aardio
//伪数组
var pseudoArray = {
	
	@{ 
		length = 20;
		_startIndex = 10;
		_get = lambda (i) i * 2;
		_set = function(i,v){ error("只读数组")}
	}
}

//遍历数组
for i,v in table.eachIndex(pseudoArray){
	print(i,v);
}

//JSON 库兼容伪数组
import JSON;
print( JSON.stringify(pseudoArray) );

//将伪数组转换为纯数组
var pureArray = table.parseValue(pseudoArray);
print(pureArray);
```

## 在函数参数中构造表  <a id="table-param" href="#table-param">&#x23;</a>

函数调用参数有且只有一个使用 `{}` 构造器构建的 table 参数（并且表不是结构体）时，并且第一个出现的成员是用 `=` 分隔的名值对（并且键名不在 `[]` 内），就可以省略外层的 `{}` 。  

- 如果参数表是一个结构体则不能省略外层的 `{}`。
- 如果参数表使用类 JavaScript 语法用冒号分隔键值对，则不能省略外层的外层的 `{}` 
- 如果首个键名出现在 `[]` 内 则不能省略外层的 `{}` 。

例如以下 aardio 代码：

```aardio
func( { k = 123, k2=456, 123 }  )
```
  
可以省略外层的 `{}` 写成如下格式：

```aardio
func( k = 123, k2 = 456, 123  )
```

注意上面的参数是一个表对象，虽然这样看起来很像其他编程语言里的"命名参数"，但实际上 aardio 里没有"命名参数"。

但 aardio 确实可以用这种语法模拟其他语言的命名参数，例如 aardio 调用 Python 的示例：

```aardio
import py3;

var requests = py3.import("requests");
var ses = requests.Session();
var res = ses.$get(verify=false,"https://www.aardio.com");
```

上面的代码 `ses.$get(verify=false,"https://www.aardio.com")` 里面就用 `verify=fals` 指定了命名参数。

aardio 实际上是执行了  `ses.$get( { verify=false,"https://www.aardio.com" } )` ，传过去的调用参数实际是一个表对象，然后由 aardio 的 py3 扩展库内部转换为了 Python 的命名参数。

## 在控制台中输出表

print 函数默认会将第一个参数指定的纯数组序列化后输出，前提是 [print](../../builtin-function/print.html) 函数未被重写且未进入 HTML 模板输出模式。其他参数或其表对象仅调用 tostring 转换为字符串后输出。

示例：

```aardio
//在控制台输出 [1,2,3]
print([1,2,3]); 

//在控制台输出类似 table: 041405C8 的字符串
print({k=1;k2=2;k3=3}); 
```

console.log 函数会将所有参数中的纯数组序列化后输出，其他参数仅调用 tostring 转换为字符串后输出。

示例：

```aardio
import console;
console.log([1,2,3]); //输出 [1,2,3] 
console.log({k=1;k2=2;k3=3}); //仅输出类型与地址

console.pause();
```

console.dump 函数默认会序列化并输出所有表参数, 参数为 .NET 对象时或 COM 对象时则会调用 com.DumpTypeInfo 获取并输出对象的 COM 类型信息。

示例：

```aardio
import console;
console.dump([1,2,3]); //输出 [1,2,3] 
console.dump({k=1;k2=2;k3=3}); //序列化后输出，列出所有键值

console.pause();
```

console.dumpTable() 函数则会调用 util.table.tostring 序列化并且添加格式化缩进后输出表的值，util.table.tostring 仅转换表或嵌套表中的文本、数值、布尔值等纯值对象。

console.dumpJson() 函数则会将表转换为带有格式化缩进的 JSON 然后再输出到控制台。这个函数的输出的缩进格式可读性更好一些，但仅输出 JSON 支持的数据类型，JSON 区分数组与对象，对于 table.isArrayLike() 函数判断为数组、纯数组、类数组的对象则转换为 JSON 数组。

console.varDump() 函数则会调用 tostring 函数将表的所有键值对转换为字符串输出，这个函数不会输出嵌套表包含的内容，也不会调用序列化函数。但 console.varDump() 会输出表的全部键值，不会有任何遗漏，并且 console.varDump() 会输出结构体的大小 。

## 访问表成员

在访问表中的元素时，可以用元素的键作为下标查询元素的值，例如:  
  
```aardio
//创建表
var tab = {}; 

//下标为字符串"x"，键名为字符串"x"，值为数值200。
tab["x"] = 200; 

/下标为数值1,值为字符串 "Hello, World!"
tab[1] = "Hello, World!"; 
```   

如果键名是合法的标志符，则可以用"."成员符访问。例如 `tab["x"] = 200` 也可以写为 `tab.x = 200`。

也可以使用直接下标 `[[]]` 读写表的成员，直接下标使用两对中括号包含要查询的索引表达式。对于一般的表直接下标与下标的作用是一样的，区别在于普通下标（ `[]` ）或成员操作符（ `.` ）会触发表的元方法（如果定义了元方法），而直接下标（  `[[]]` ）不会触发表的[元方法](meta.html)。

示例：

```aardio
//创建表
var tab = { 
	
	//定义元表 
	@{
		_get = function(k){
			return k + "的值"
		}
	}
 }; 

import console

//输出了 "xyz的值"
console.log( tab["xyz"] );

//无输出值，直接下标不触发元方法
console.log( tab[["xyz"]] )

console.pause()
```


当我们将表元素赋值为 null 会删除这个元素。例如：

```aardio
tab.x = null; //删除tab.x
```

## 多项索引 <a id="multiple-indexing" href="#multiple-indexing">&#x23;</a>

aardio 允许在下标操作符中用逗号分隔多个索引值。

示例：

```aardio
multiDimensionalArray[0,0] = "值"
```

上面的代码等价于下面的代码：

```aardio
//注意方括号中间有一个空格以避免包含数组的双层 [] 被识别为直接下标操作符。
multiDimensionalArray[ [0,0] ] = "值"
```

aardio 的多项索引实际等价于构造一个纯数组（  [pure-array](../datatype/table/_.html#pure-array-table) ）作为索引。

> 下标包含多项索引时第一个索引必须是单个标识符或字面值而不能是复合表达式，后面的其他索引则无限制。例如  `obj[row,col]` 语法正确，而 `obj[pos.row,pos.col]` 存在语法错误，改为 `obj[ [pos.row,pos.col] ]` 就不会报错了。

目标对象可以在 `_get` 或 `_set` 元方法自定义对象如何处理这样的多项索引（ 也就是以数组为索引 ）。

aardio 中的 COM 接口、.NET 接口已经自动支持上述的多项索引，当索引为数组时 COM 与 .NET 接口都可以自动将其展开为多维索引。

更多关于下标操作符与多项索引的内容请参考：[下标操作符，多项索引](../../operator/member-access.html)

## 表的只读成员 <a id="readonly-member" href="#readonly-member">&#x23;</a>

表对象（ table ）的成员名称如果首字符为下划线，并且长度大于 1 个字节并小于 256 个字节，则是一个只读成员（ readonly member ）。

示例：

```aardio
var tab = {
	_readonlyMember = "值不可修改";
	member = "值可以修改";
}

//没有修改值，被忽略
tab._readonlyMember = "值不可修改";

//不能修改只读成员，报错。
tab._readonlyMember = "新的值";
```

要特别注意，表的只读成员并不要求是合法标识符，例如： 

```aardio
tab["_ (不是合法标识符)"] = 1;
tab["_ (不是合法标识符)"] = 1234;//报错，不能修改常量
```  

在对象的元表中指定元属性 `_readonly = false` 可禁用读成员保护，这样就可以自由修改名字以下划线开头的表成员。如果在表对象的元表中不设置 `_readonly` 则默认启用只读成员保护（所有名字以下划线开头的成员禁止修改非 null 值）。  

如果元表中设置  `_readonly`  为任何非 null 值都会被强制转换为 false,  该值一旦设置以后即不可修改，如果希望启用只读成员保护，唯一的方法就是不设置该属性。  

全局表（ global ）将元属性 `_readonly` 设置为任何值都会被忽略，global 的只读成员保护总是启用状态。  

请参考：  
* [_readonly 元属性](meta.html#_readonly)  
* [命名空间常量](../../variables-and-constants.html#namespace-constant)  

## 遍历表

遍历表中的全部元素  
  
```aardio
import console; 

var tab = { a111="字符串"; b2=123; c="字符串2"; d=23; e=56; 78; 99; 123; 0 }

for k,v in tab  { 
    /*
    k为键,v 是匹配的值,
    在这里键值并不会按上面创建表的键值顺序输出
    */
    console.log(k,v);
};

console.pause(true);
```  

如果表只包含有序数组（“键”是从1开始、有序、连续的数值），则 for in 语句会按数组顺序循环。  
  
如果表还包含非数组成员，则需要改用计数循环才能顺序遍历数组成员。示例：

```aardio
import console; 

var array = { 
    a="字符串";b=123;c="字符串2";d=23;e=56; 
    78; 99; 123; 0 
}

//使用 #array 取数组长度
for( i=1; #array;1){ 
    //i为当前数值索引,tab[ i ]为当前值
    console.log( i ,array[ i ] );
}

//也可以用 while var 语句模拟 for 循环语句遍历数组
while( var i = 0; i++ ; i <= #array ) {
   console.log( i ,array[ i ] );
}

console.pause(true);
```  

遍历稀疏数组的方法请参考： [table.eachArgs() 示例](#eachArgs) 。

## 排序数组 <a id="sort" href="#sort">&#x23;</a>


使用 table.sort 可以重新排序有序数组（不适用于稀疏数组）。

函数原型：

`table.sort(array[, compareProc])`

说明:

排序 array 参数指定的数组，此函数无返回值。

默认较小的值排在前面，可选用 compareProc 参数自定义比较函数。

比较函数应使用 `<` 或 `>`操作符比较元素的大小。两元素相等时不能返回 true，因此应避免使用 `<=` 或 `>=` 操作符比较大小。

compareProc 需要比较的是 owner 参数与第一个参数，返回 true 表示 owner 应排序在前。

示例：

```aardio
var array = {1,2,3}

//排序
table.sort(array,function(next){
	//较大的值应排序在前
	return owner > next
})
```

上面的自定义排序结果为 `{3,2,1}` , 较大的值排序在前。

## 如何排序遍历哈希表的成员 <a id="eachName" href="#eachName">&#x23;</a>

哈希表的用途是快速查找值，无序正是他的最大优点（索引快如闪电）。  
数组的排序功能与表的快速查找值的优势是可以相互结合的。  
  
示例：

  
```aardio
import console; 

//无序的哈希表
var tab = {
    c = "3";
    b = "2";
    a = "1";
}

//有序的数组
var keys = {
    "c","b","a"
}

//用数组排序键名
table.sort(keys)

//顺序遍历键名数组
for(i,k in keys){

    //再到哈希表中去查找值
    console.log(k,tab[k])
}

console.pause(true);
```  

aardio 提供了一个原理类似的 table.eachName 函数可以对表的键名排序然后遍历表的所有元素。

示例：
 
```aardio
import console; 

//无序的哈希表
var tab = {
    c = "3";
    b = "2";
    a = "1";
}

//排序哈希表
for k,v in table.eachName(tab){
	console.log(k,v)
}

console.pause(true);
```

table.eachName 函数可以指定排序函数与键名数组，具体请参考该函数[参考文档](https://www.aardio.com/zh-cn/doc/library-reference/table/_.html)。

## 合并与展开数组 <a id="unpack" href="#unpack">&#x23;</a>


我们可以把多个变量用一对花括号包含起来以转换为数组，也可以使用 table.unpack 函数将数组转换为多个变量。

示例：

```aardio
import console;

var func = function(a,b,...){
	
	//将多个变量与不定参数合并为数组，返回多个值的 ... 必须放在最后面
	var args = {a,b,...}
	
	//展开数组
	var a,b,c,d = table.unpack(args);
	
	//合并返回值，返回多个值的 table.unpack(args) 必须放在最后面
	var args2  = {a,b,table.unpack(args)}
	
	//返回多个值
	return table.unpack(args2)
}

//合并函数的多个返回值到数组
var arr = { func(1,2,3,4,5) }

//输出数组
console.dumpTable(arr);

console.pause();
```  