我正在尝试了解#
运算符的规则。
请考虑以下示例:
> t1 = table.pack(nil, 1, nil, 2)
> #t1
4
> for k, v in pairs(t1) do print(k, v) end
2 1
4 2
n 4
> t2 = table.pack(nil, 1, nil, 2, nil)
> #t2
2
> for k, v in pairs(t2) do print(k, v) end
2 1
4 2
n 5
> t3 = table.pack(nil, 1, nil, 2, nil, 3, nil)
> #t3
0
> for k, v in pairs(t3) do print(k, v) end
2 1
4 2
6 3
n 7
我不能为此做任何事情。 n
字段的值始终与最初传递给table.pack
的参数数目匹配,但是#
运算符返回的值无处不在,如以下摘要所示:
| n | # |
|---+---|
| 4 | 4 |
| 5 | 2 |
| 7 | 0 |
#
的规则是什么?换句话说,如果我看到来自
for k, v in pairs(sometable) do print(k, v) end
...如何推断#sometable
返回的值?
(如果有问题,我正在使用lua5.3
。)
编辑:对于上述示例中的所有表t1
,t2
和t3
,我应该添加ipairs
返回<没什么。例如
> for i, v in ipairs(t0) do print(i, v) end
> -- no output
答案 0 :(得分:7)
Lua中的数组和nil
是一个复杂的话题。
使用table.pack
时,它将计算参数数量并将表n
的值设置为此。如果您使用{1, nil, 3, nil}
之类的表构造函数,则不是这种情况。
#
运算符的作用稍有不同。
来自manual:
应用于表的长度运算符返回该表中的边框。表
t
中的边框是表中任何自然索引,其中非nil值后跟nil值(当索引1为nil时为零)。
那是对桌子的意思
t = {1, nil, 3, nil}
它可能返回1
,因为t[1]
为1并且t[2]
为nil,或者返回3
,因为t[3]
是3并且{{1} }为零。
t[4]
函数做的另一件事:它实际上从1开始对索引进行计数,直到它在表中达到零为止,因此它将一直计数到第一个< em> border 。
如果您希望ipairs()
运算符返回表的#
值,那么在Lua 5.2或更高版本中,您可以执行以下操作:
n
但是请注意,这不适用于基于Lua 5.1的LuaJIT。
您可以通过设置表local t = {n=10}
setmetatable(t, {__len=function(self) return self.n end})
print(#t) -- Will print 10, even though t has 0 numeric indices
的{{1}}元方法,使__ipairs
从t
到ipairs(t)
计数,而不是从第一个nil元素计数。
编辑:至于为什么1
对您的示例不执行任何操作,这主要是由于我已经对ipairs进行了解释,但是由于表中的第一个键为t.n
,因此它立即假定其为空因此什么也没做。
这并不是很实际,因为您不应该依赖于这种特定于实现的行为,但这是PUC Lua 5.3如何为
实现ipairs
运算符
nil
如您所见,Lua使用二进制搜索来找到边界,只有当您假设只有一个边界时才有意义。否则,它可能会随机跳过第一个,而只是通过向表中添加一个值而突然发现它。
还要记住,每次添加值时,表的数组部分不会增加。如果我没记错的话,它总是翻倍。因此,您可以有一个4元素表,添加一个元素,Lua会将数组大小增加到8。添加另外4个元素,即使只有9个元素,它也会增长到16。这是为了避免不必要的分配。
答案 1 :(得分:1)
总结:由于Lua不支持在表中存储nil,因此它将一直计数到非nil元素之一。您需要改为使用ipairs进行手动计数。 Here is the reference