此第一版是为 Lua 5.0 编写。虽然对后续版本仍然非常相关,但存在一些差异。
第四版针对 Lua 5.3,可在 亚马逊 和其他书店购买。
通过购买本书,您还可以帮助 支持 Lua 项目


2.5 – 表

表类型实现关联数组。关联数组是一种数组,它不仅可以用数字索引,还可以用字符串或语言的任何其他值(nil 除外)索引。此外,表没有固定大小;您可以根据需要动态地向表中添加任意多个元素。表是 Lua 中的主要(事实上是唯一的)数据结构化机制,而且非常强大。我们使用表以简单、统一且高效的方式表示普通数组、符号表、集合、记录、队列和其他数据结构。Lua 也使用表来表示包。当我们编写 io.read 时,我们的意思是“io 包中的 read 条目”。对于 Lua,这意味着“使用字符串 "read" 作为键对表 io 进行索引”。

Lua 中的表既不是值也不是变量;它们是对象。如果您熟悉 Java 或 Scheme 中的数组,那么您对我们的意思会有一个公平的了解。但是,如果您对数组的理解来自 C 或 Pascal,您必须稍微开阔一下思路。您可以将表视为一个动态分配的对象;您的程序仅操作它们的引用(或指针)。在幕后没有隐藏的副本或新表的创建。此外,您不必在 Lua 中声明一个表;事实上,没有办法声明一个表。您可以通过构造函数表达式创建表,其最简单的形式写为 {}

    a = {}     -- create a table and store its reference in `a'
    k = "x"
    a[k] = 10        -- new entry, with key="x" and value=10
    a[20] = "great"  -- new entry, with key=20 and value="great"
    print(a["x"])    --> 10
    k = 20
    print(a[k])      --> "great"
    a["x"] = a["x"] + 1     -- increments entry "x"
    print(a["x"])    --> 11
表始终是匿名的。持有表的变量与表本身之间没有固定的关系
    a = {}
    a["x"] = 10
    b = a      -- `b' refers to the same table as `a'
    print(b["x"])  --> 10
    b["x"] = 20
    print(a["x"])  --> 20
    a = nil    -- now only `b' still refers to the table
    b = nil    -- now there are no references left to the table
当程序不再引用某个表时,Lua 内存管理最终会删除该表并重新利用其内存。

每个表都可以存储具有不同类型索引的值,并且会根据需要容纳新条目而增长

    a = {}     -- empty table
    -- create 1000 new entries
    for i=1,1000 do a[i] = i*2 end
    print(a[9])    --> 18
    a["x"] = 10
    print(a["x"])  --> 10
    print(a["y"])  --> nil
请注意最后一行:与全局变量一样,如果表字段未初始化,则其值为 nil。同样与全局变量一样,您可以将 nil 分配给表字段以将其删除。这不是巧合:Lua 将全局变量存储在普通表中。有关此主题的更多信息,请参见 第 14 章

要表示记录,您可以使用字段名称作为索引。Lua 通过提供 a.name 作为 a["name"] 的语法糖来支持此表示。因此,我们可以以更简洁的方式编写上一个示例的最后几行,如下所示

    a.x = 10                    -- same as a["x"] = 10
    print(a.x)                  -- same as print(a["x"])
    print(a.y)                  -- same as print(a["y"])
对于 Lua,这两种形式是等效的,并且可以自由混合使用;但对于人类读者,每种形式可能表示不同的意图。

初学者常犯的一个错误是将 a.xa[x] 混淆。第一种形式表示 a["x"],即由字符串 "x" 索引的表。第二种形式是按变量 x 的值索引的表。请看区别

    a = {}
    x = "y"
    a[x] = 10                 -- put 10 in field "y"
    print(a[x])   --> 10      -- value of field "y"
    print(a.x)    --> nil     -- value of field "x" (undefined)
    print(a.y)    --> 10      -- value of field "y"

要表示常规数组,您只需使用具有整数键的表。没有办法声明其大小;您只需初始化所需的元素

    -- read 10 lines storing them in a table
    a = {}
    for i=1,10 do
      a[i] = io.read()
    end
当您遍历数组的元素时,第一个未初始化的索引将导致 nil;您可以使用此值作为哨兵来表示数组的结尾。例如,您可以使用以下代码打印上一个示例中读取的行
    -- print the lines
    for i,line in ipairs(a) do
      print(line)
    end
Lua 基本库提供了 ipairs,这是一个方便的函数,允许您遍历数组的元素,遵循数组在其第一个 nil 元素处结束的约定。

由于你可以使用任何值对表进行索引,因此你可以从你喜欢的任意数字开始数组的索引。然而,在 Lua 中通常从一(而不是像在 C 中那样从零)开始数组,并且标准库坚持此惯例。

由于我们可以使用任何类型对表进行索引,因此在对表进行索引时,我们遇到了与相等性中出现的相同细微差别。虽然我们可以使用数字 0 和字符串 "0" 对表进行索引,但这两个值(根据相等性)是不同的,因此表示表中的不同位置。同样,字符串 "+1""01""1" 都表示不同的位置。当对索引的实际类型有疑问时,请使用显式转换以确保

    i = 10; j = "10"; k = "+10"
    a = {}
    a[i] = "one value"
    a[j] = "another value"
    a[k] = "yet another value"
    print(a[j])            --> another value
    print(a[k])            --> yet another value
    print(a[tonumber(j)])  --> one value
    print(a[tonumber(k)])  --> one value
如果你不注意这一点,你可能会在你的程序中引入细微的错误。