aardio 文档

表( table )

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

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

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

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

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

基础知识

哈希表与有序数组

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

  1. 哈希表(无序集合)

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

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

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

    在遍历哈希表时的顺序并不一定会保持代码中书写、添加表成员的顺序,因此我们把这种集合称为无序集合。哈希表的优势是查找速度非常快,即使表包含的成员非常多,仍然可以快速地访问指定的成员。

    下面是一个创建哈希表的例子:

    var tab = {
        a = 123;
        str = "字符串";
        [123] = "不符合变量命名规则的键名应放在下标内。";
        ["键 名"] = "不符合变量命名规则的键名应放在下标内。";
        键名 = {
            test = "表也可以包含表";
        }
    }
    

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

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

    var tag ={"name1":123,"name2":456}
    
  2. 数组(有序集合) #

    如果表中不定个数的成员的“键”是从1开始、有序、连续的数值,那么这些成员构成一个有序数组。

    创建有序数组的示例:

    var array = { 
        [1] = 123;
        [2] = "数组的值可以是任何其他对象"; 
        [3] = { "也可以嵌套包含其他表或数组"}
    } 
    

    上面我们将数值键放在下标[]中,用分号;分隔数组成员(也可以使用逗号,分隔数组成员),用等号=分隔键值对。

    在创建数组时,数组的键可以省略不写。有序数组的键可以省略,示例:

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

    有序数组可以使用 for 循环( 或者 table.eachIndex )有序地遍历成员,示例:

    import console; 
    
    //在表中创建数组
    var array = { 
        [1] = 123;
        [2] = "数组的值可以是任何其他对象"; 
        [3] = { "也可以嵌套包含其他表或数组"}
    } 
    
    //数组的键可以省略,下面这样写也可以(并且建议省略)
    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();
    

    表总是可以同时包含数组、以及非数组成员,即使不包含数组成员,我们也可以将表作为空数组处理。

稀疏数组 #

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

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

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

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

至于稀疏数组我们将其作为一般的哈希表操作就可以了,不要用数组函数去操作稀疏数组。

下面的数组就包含了 null 值,属于数值键(索引)不连续的稀疏数组:

var sparseArray = { "值:1", null, "值:2", "值:4", null, null }

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

var sparseArray = { "值:1", null, "值:2", "值:4" }

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

var sparseArray = {
    [1] = "值:1";
    [2] = "值:2";
    [4] = "值:4";    
}

这种稀疏数组不应当作为有序数组使用,也不应当使用 # 操作符去获取稀疏数组的长度。 s

稀疏数组可以用 table.range() 获取最小索引、最大索引。
也可以使用 table.eachArgs() 遍历稀疏数组。

示例: #

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 数组

构造表 #

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

import console; 

//空表
var tab = {}

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

//数组
var array = {
    123;456;789;"其他值"
}


console.pause(true);

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

如果表的键名不符合标识符命名规则,则必须写在下标内,例如:

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

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

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

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

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

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

在函数参数中构造表

当在函数内部有只有一个使用 {} 构造器构建的 table 参数(并且表不是结构体)时,并且第一个出现的成员是用 = 分隔的键值对(不能用冒号替代),就可以省略外层的 {}

例如以下 aardio 代码:

func( { k = 123, k2=456, 123 }  )

可以省略外层的 {} 写成如下格式:

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

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

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

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 的命名参数。

访问表成员

在访问表中的元素时,可以用元素的键作为下标查询元素的值,例如:

//创建表
var tab = {}; 

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

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

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

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

示例:

//创建表
var tab = { 

    //定义元表 
    @{
        _get = function(k){
            return k + "的值"
        }
    }
 }; 

import console

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

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

console.pause()

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

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

表的只读成员 #

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

示例:

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

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

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

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

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

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

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

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

请参考:

遍历表

遍历表中的全部元素

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 语句会按数组顺序循环。

如果表还包含非数组成员,则需要改用计数循环才能顺序遍历数组成员。示例:

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() 示例

排序数组 #

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

函数原型:

table.sort(array[, compareProc])

说明:

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

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

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

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

示例:

var array = {1,2,3}

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

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

如何排序遍历哈希表的成员 #

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

示例:

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 函数可以对表的键名排序然后遍历表的所有元素。

示例:

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 函数可以指定排序函数与键名数组,具体请参考该函数参考文档

合并与展开数组 #

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

示例:

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();

Markdown 格式