表教程

lua-users home
wiki

表是 Lua 中唯一的“容器”类型。它们是关联数组([1]),这意味着它们存储一系列键/值对。在键/值对中,您可以将一个值存储在一个键下,然后稍后使用该键检索该值。

创建表

表使用表构造函数创建,表构造函数使用花括号定义,例如 {}。要定义一个空表,我们可以这样做。

> t = {}    -- construct an empty table and assign it to variable "t"
> print(t)
table: 0035AE18
请注意,当显示表的值时,仅显示对象的类型和唯一 ID。要打印出表的内容,我们必须显式地这样做。稍后我们将学习如何做到这一点。

使用表

要访问表中与键关联的值,您可以使用 table[key] 语法

> t = {}
> t["foo"] = 123 -- assign the value 123 to the key "foo" in the table
> t[3] = "bar" -- assign the value "bar" to the key 3 in the table
> = t["foo"]
123
> = t[3]
bar

如果不存在与键关联的值,则不会出错,而是结果将为 nil

> t = {}
> = t["foo"]
nil

您可以通过将 nil 赋给值来从表中删除键/值对。

> t["foo"] = nil

任何值都可以用作键(不仅仅是数字和字符串),只需确保它不是 nil 或 NaN(非数字)

> t = {}
> k = {}
> f = function () end
> t[k] = 123
> t[f] = 456
> = t[k]
123
> = t[f]
456
> t[nil] = 123
stdin:1: table index is nil
stack traceback:
        stdin:1: in main chunk
        [C]: in ?
> t[0/0] = 123
stdin:1: table index is NaN
stack traceback:
        stdin:1: in main chunk
        [C]: in ?

但是,使用字符串常量作为键非常常见,因此有一个特殊的快捷语法

> t = {}
> t.foo = 123 -- same as t["foo"] (but not t[foo], which would use the variable foo as the key)
> = t.foo
123
> = t["foo"]
123
仅当字符串由下划线、字母和数字组成,但不以数字开头时,快捷语法才有效。

您也可以在 {} 语法内直接添加键/值关联

> t = {["foo"] = "bar", [123] = 456}
> = t.foo
bar
> = t[123]
456

对于 {} 中的字符串键,也有一个快捷语法,只要字符串符合 . 语法的相同规则即可。

> t = {foo = "bar"} -- same as ["foo"]="bar" (but not [foo]="bar" , that would use the variable foo)
> = t["foo"]
bar

要遍历表中的所有键/值对,请使用 pairs 迭代器

> t = {foo = "bar", [123] = 456}
> for key,value in pairs(t) do print(key,value) end
foo bar
123 456
使用 pairs 遍历的顺序是未定义的。仅仅因为您在一个项之后添加了另一个项,并不意味着它们就会以该顺序出现在 pairs 中。

pairs 循环内部,重新分配现有键或删除它们(通过将 nil 赋给它们)是安全的,但不能添加新键(以前具有 nil 值)。

表作为数组

首先请记住,表仍然只是键/值容器,因为 Lua 实际上没有数组类型。但是表可以像数组一样处理,这里对此进行了说明。

表构造函数可以包含逗号分隔的对象列表来创建一个“数组”。

> t = {"a", "b", "c"}
> = t[1]
a
> = t[3]
c
这是的语法快捷方式
> t = {[1]="a", [2]="b", [3]="c"}
> = t[1]
a
> = t[3]
c
所以它仍然只是一个键/值关联。

您还可以将数组语法与常规的 key=value 语法混合使用。

> t = {"a", "b", [123]="foo", "c", name="bar", "d", "e"}
> for k,v in pairs(t) do print(k,v) end
1       a
2       b
3       c
4       d
5       e
123     foo
name    bar

第一个索引是数字一,与大多数从数字零开始的其他语言不同。选择这个的原因是它通常更直观。选择它也是因为它只是一个键,而不是从开头偏移。大多数语言都实现了实际的数组类型。

您可以使用 # 运算符获取数组的“长度”。

> t = {"a", "b", "c"}
> = #t
3

# 运算符不会计算表中的所有项(!)。它会查找最后一个整数(非分数)键。由于其实现方式,如果表中的所有整数键都不连续,则其结果是未定义的。这就是为什么不应将其用于用作稀疏数组的表([2])。

有两种方法可以将一个项添加到数组的末尾。

> t = {}
> table.insert(t, 123)
> t[#t+1] = 456
> = t[1]
123
> = t[2]
456

table.insert 接受一个可选的索引参数以插入到数组的中间。它会将索引以上的任何其他整数键向上移动。

> t = {"a", "c"}
> table.insert(t, 2, "b")
> = t[1], t[2], t[3]
a b c

table.remove 从数组中删除一个项,并将任何剩余的整数键向下移动。

> t = {"a", "b", "c"}
> table.remove(t, 2)
> = t[1], t[2]
a c

要遍历数组,请使用 ipairs。与 pairs 不同,它只为您提供从 1 开始的连续整数键。它保证了它们的顺序序列。使用 pairs 时,数字键不一定以正确的顺序给出!

> t = {"a", "b", "c"}
> for i, v in ipairs(t) do print(i, v) end
1       a
2       b
3       c

要连接字符串数组,可以使用 table.concat。它接受一个可选的分隔符、起始和结束参数。这里我们只使用分隔符。

> t = {"a", "b", "c"}
> = table.concat(t, ";")
a;b;c

有关所有 table.* 函数及其完整文档的列表,请参阅 [[3]]。

表值是引用

当您将表传递给函数或将其存储在新变量中时,不会创建该表的新副本。在这种情况下,表不像数字那样工作。相反,变量或函数成为对原始表的引用。这非常类似于 C 语言中的指针。例如

> t = {}
> u = t
> u.foo = "bar"
> = t.foo
bar
> function f(x) x[1] = 2 end
> f(t)
> = u[1]
2

表在最后一个引用消失后会被垃圾收集器从内存中释放。然而,这并不总是立即发生。即使表(直接或间接)包含对自身的引用,垃圾收集器也能正常工作。

另一个需要记住的相关事项是,表比较是通过引用进行的。使用 == 比较表将返回 false,即使两个表具有相同的内容。它们必须是同一个表的引用。

最后;如果您想复制一个表,您将不得不手动复制。Lua 不提供标准的复制函数,主要是因为复制表的方式多种多样。

表作为无序集合

通常,Lua 新手会创建一个数组来存储一组对象,即使顺序不是必需的。这样做的问题是删除很慢,因为计算机需要将其他项向下移动。检查一个项是否在数组中很慢;同样,因为计算机必须遍历所有项。

这可以通过将项存储在键中并将值设置为虚拟值(如 true)来解决。这样做将有助于您使用表作为无序集合,并具有快速的插入、删除和查找。

主要区别在于没有简单的方法可以获得计数(您必须使用循环),并且您不能在集合中存储相同的项两次。

因此,如果您需要存储一组项,最好考虑集合和数组,以查看哪种最适合您的情况。

local items = {}

-- add some items to the set
items["foo"] = true
items[123] = true

-- is "foo" in the set?
if items["foo"] then
  -- do stuff
end

-- remove item from the set
items[123] = nil

RecentChanges · preferences
编辑 · 历史
最后编辑于 2020 年 9 月 23 日下午 2:38 GMT (差异)