#运算符的规则

时间:2019-07-15 09:35:00

标签: lua

我正在尝试了解#运算符的规则。

请考虑以下示例:

> 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。)


编辑:对于上述示例中的所有表t1t2t3,我应该添加ipairs返回<没什么。例如

> for i, v in ipairs(t0) do print(i, v) end
> -- no output

2 个答案:

答案 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}}元方法,使__ipairstipairs(t)计数,而不是从第一个nil元素计数。


编辑:至于为什么1对您的示例不执行任何操作,这主要是由于我已经对ipairs进行了解释,但是由于表中的第一个键为t.n,因此它立即假定其为空因此什么也没做。


这并不是很实际,因为您不应该依赖于这种特定于实现的行为,但这是PUC Lua 5.3如何为

实现ipairs运算符
nil

如您所见,Lua使用二进制搜索来找到边界,只有当您假设只有一个边界时才有意义。否则,它可能会随机跳过第一个,而只是通过向表中添加一个值而突然发现它。

还要记住,每次添加值时,表的数组部分不会增加。如果我没记错的话,它总是翻倍。因此,您可以有一个4元素表,添加一个元素,Lua会将数组大小增加到8。添加另外4个元素,即使只有9个元素,它也会增长到16。这是为了避免不必要的分配。

答案 1 :(得分:1)

我问了similar question

总结:由于Lua不支持在表中存储nil,因此它将一直计数到非nil元素之一。您需要改为使用ipairs进行手动计数。 Here is the reference