为啥 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-跟随键(即 it[i] ~= nilt[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 的长度 (#) 运算符会返回意外的值?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在三元运算符中使用“0”会返回第一个值?

为啥使用 Pandas 的 max 和 min 函数会返回意外结果?

当从 `torch.class` 方法将`self` 返回到控制台时,为啥 Lua/Torch 会耗尽内存?

javascript:使用三元运算符的意外评估行为

Lua运算符,为啥不定义+=、-=等?

Lua常用的文件操作