表教程 |
|
表使用表构造函数创建,表构造函数使用花括号定义,例如 {}。要定义一个空表,我们可以这样做。
> t = {} -- construct an empty table and assign it to variable "t"
> print(t)
table: 0035AE18
要访问表中与键关联的值,您可以使用 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