#运算符的规则

kjo*_*kjo 3 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
Run Code Online (Sandbox Code Playgroud)

我不能为此做头或尾。该n字段的值始终与最初传递给的参数数量匹配table.pack,但#运算符返回的值无处不在,如以下摘要所示:

| n | # |
|---+---|
| 4 | 4 |
| 5 | 2 |
| 7 | 0 |
Run Code Online (Sandbox Code Playgroud)

规则是#什么?换句话说,如果我看到来自

for k, v in pairs(sometable) do print(k, v) end
Run Code Online (Sandbox Code Playgroud)

...我该如何推导返回的值#sometable

(如果有关系,我正在使用lua5.3。)


编辑: 我要补充的是,所有表t1t2以及t3在上面的例子中ipairs回报什么。例如

> for i, v in ipairs(t0) do print(i, v) end
> -- no output
Run Code Online (Sandbox Code Playgroud)

Dar*_*yer 7

nilLua中的Array和s是一个复杂的话题。

当您使用时table.pack,它将计算参数数量并将表n值设置为该值。如果您使用类似的表构造函数,则不是这种情况{1, nil, 3, nil}

#运营商,但是,做了略有不同。

手册

应用于表的长度运算符返回该表中的边框。表格中的边框是表格t中的任何自然索引,其中非nil值后跟一个nil值(当索引1为nil时为零)。

这就是说,对于桌子

t = {1, nil, 3, nil}
Run Code Online (Sandbox Code Playgroud)

它可能返回1,因为t[1]是1且t[2]为nil,或者3,因为t[3]是3且t[4]为nil。

ipairs() 函数做的另一件事是:它实际上从1开始对索引进行计数,直到它在表中达到零为止,因此它将始终计数到表的第一个边界

如果要让#操作员返回n表的值,请在Lua 5.2或更高版本中执行以下操作:

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
Run Code Online (Sandbox Code Playgroud)

但是请注意,这在基于Lua 5.1的LuaJIT中不起作用。

同样,您可以设置__ipairs表的元方法,t以便ipairs(t)从计数到第一个nil元素1t.n而不是第一个nil元素。


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


这并不是真正相关,因为您不应该依赖于此类特定于实现的行为,但是这是PUC Lua 5.3如何实现以下#操作的操作符:

t = {1, nil, 3, nil}
Run Code Online (Sandbox Code Playgroud)

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

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