为啥 Lua 的长度 (#) 运算符会返回意外的值?
Posted
技术标签:
【中文标题】为啥 Lua 的长度 (#) 运算符会返回意外的值?【英文标题】:Why does Lua's length (#) operator return unexpected values?为什么 Lua 的长度 (#) 运算符会返回意外的值? 【发布时间】:2014-06-28 17:52:01 【问题描述】:Lua 有 # 运算符来计算用作数组的表的“长度”。 我检查了这个运算符,我很惊讶。
这是我在 Lua 5.2.3 下运行的代码:
t = ;
t[0] = 1;
t[1] = 2;
print(#t); -- 1 aha lua counts from one
t[2] = 3;
print(#t); -- 2 tree values, but only two are count
t[4] = 3;
print(#t); -- 4 but 3 is mssing?
t[400] = 400;
t[401] = 401;
print(#t); -- still 4, now I am confused?
t2 = 10, 20, nil, 40
print(#t2); -- 4 but documentations says this is not a sequence?
谁能解释一下规则?
【问题讨论】:
解释规则是设计师的工作。他们在documentation 中这样做了。 (并非所有文档都对学习有用,但学习应该从这里开始并继续。) 【参考方案1】:引用 Lua 5.2 参考手册:
只有当表是一个序列时才定义表 t 的长度,也就是说,对于某个整数 n,它的正数字键的集合等于 1..n
#
运算符对非序列的结果是 undefined。
但是what happens in C implementation of Lua when we call #
on a non-sequence?
背景:Lua 中的表在内部分为数组部分和哈希部分。那是一种优化。 Lua 试图避免经常分配内存,所以它预先分配了 2 的下一个幂。这是另一个优化。
-
当数组部分的最后一项是
nil
时,#
的结果是通过binsearch数组部分找到第一个nil-followed key找到的最短有效序列的长度。
当数组部分最后一项不是nil
且哈希部分为空时,#
的结果就是数组部分的物理长度。
当数组部分的最后一项不是nil
并且哈希部分不为空时,#
的结果是通过对哈希部分进行binsearch找到的最短有效序列的长度为第一个nil-跟随键(即 i
和 t[i] ~= nil
和 t[i+1] == nil
的正整数),假设数组部分充满了非空值(!)。
所以#
的结果几乎总是最短有效序列的(所需)长度,除非数组部分中表示非序列的最后一个元素是非零。然后,结果比预期的要大。
这是为什么呢?这似乎是又一次优化(对于大小为 2 的数组)。此类表上#
的复杂度为O(1)
,而其他变体为O(log(n))
。
【讨论】:
【参考方案2】:在 Lua 中,只有特殊格式的表才被视为数组。它们并不是真正的数组,就像人们可能认为的 C 语言中的数组一样。这些项目仍在哈希表中。但是键是数字的,并且从 1 到 N 是连续的。Lua 数组是单位偏移量,而不是零偏移量。 底线是,如果您不知道您形成的表是否符合数组的 Lua 标准,那么您必须对表中的项目进行计数才能知道表的长度。这是唯一的方法。这是一个功能:
function table_count(T)
local count = 0
for _ in pairs(T) do count = count + 1 end
return count
end
如果你用下面例子中使用的“insert”函数填充一个表,那么你将保证创建一个“数组”表。
s=
table.insert(s,[whatever you want to store])
table.insert 可以在循环中或从代码中的其他位置调用。关键是,如果你以这种方式将项目放入表中,那么它将是一个数组表,你可以使用#运算符知道表中有多少项目,否则你必须计算项目。
【讨论】:
以上是关于为啥 Lua 的长度 (#) 运算符会返回意外的值?的主要内容,如果未能解决你的问题,请参考以下文章
为啥使用 Pandas 的 max 和 min 函数会返回意外结果?