Lua Table 间隙避免

Lua Table Gap aviod

为什么 lua table(重新散列?)在我使用不同语法时避免出现空白?

inspect function

d = require "core/modules/inspect"

案例 1:标准语法第一个元素是一个间隙

t = {1,2,3}
t[1] = nil
d(t)
__________
{ nil, 2, 3 }

Case2:带括号的语法没有空隙

 t = {
    [1] = 1,
    [2] = 2,
    [3] = 3,
}
t[2] = nil
d(t)
__________
{ 1,
  [3] = 3
}

案例 3:动态数组 - 无间隙

t = {}
t[1] = 1
t[2] = 2
t[3] = 3
t[2] = nil
d(t)
__________
{ 1,
  [3] = 3
}

案例 4:第一个元素设置为 nil 时的动态数组 - 是一个间隙

t = {}
t[1] = 1
t[2] = 2
t[3] = 3
t[1] = nil
d(t)
__________
{ nil, 2, 3 }

案例 5:在第一个元素中将括号语法设置为 nil 仍然没有间隙

t = {
    [1] = 1,
    [2] = 2,
    [3] = 3,
}
t[1] = nil
d(t)
__________
{
  [2] = 2,
  [3] = 3
}

Lua 没有定义非顺序索引的长度运算符的行为,但是 Why does Lua's length (#) operator return unexpected values? 的公认答案进入了 Lua 5.2 的标准实现中实际发生的情况.也就是说,这个答案本身并不能完全解释行为,所以这里有一个个案解释,使用 Lua 5.3.5 的标准实现。

注意:我使用长度运算符来清楚地显示出现漏洞的情况。关于这如何适用于您使用的检查功能,从我所看到的它的行为来看,它只是明确地声明了范围之外的任何索引 1 <= index <= #table.

案例一:

> t = {1,2,3}
> t[1] = nil
> print(#t)
3

Lua table 的数组部分的大小基于 2 的幂 - 除了 当 table 是用预设值构建。 t = {1,2,3} 行创建了一个 table,其数组部分的大小为 3。当您执行 t[1] = nil 时,数组部分没有理由重新调整大小,因此其大小保持为 3。数组部分的最后一项非零,哈希部分为空,返回数组大小 - 3。

案例二:

> t = {[1] = 1, [2] = 2, [3] = 3}
> t[2] = nil
> print(#t)
1

如果您在 table 构造函数中定义 table 值,Lua 会将方括号内的任何键与 table 的散列部分相关联。所以,table 的数组部分实际上是空的。该实现对数组的散列部分进行二分查找,得到 1.

案例三:

> t = {}
> t[1] = 1
> t[2] = 2
> t[3] = 3
> t[2] = nil
> print(#t)
1

当在构造函数外部使用方括号时,Lua 将检查数字索引以查看它们是否应该进入数组部分或散列部分。在这种情况下,每个索引都将进入数组部分,但因为这些值是在构造函数之外定义的,所以数组的大小将根据 2 的幂进行赋值。因此,在最初的三个赋值之后,数组部分的大小将为 4。nil 赋值不会触发重新调整大小,因此它保持为 4。因此,当应用长度运算符时,它会看到最终值数组中的 space 是 nil,因此它二进制搜索第一个 nil 值之前的索引,即 1.

案例四:

> t = {}
> t[1] = 1
> t[2] = 2
> t[3] = 3
> t[1] = nil
> print(#t)
3

这种情况与情况 3 完全相同,但是二分搜索发现 t[2] 是非零的,因此不考虑 2 之前任何索引处的值。这导致搜索继续在 2 之后的索引上,得出长度为 3.

案例五:

> t = {[1] = 1, [2] = 2, [3] = 3}
> t[1] = nil
> print(#t)
0

从 table 项目是 table 的散列部分的成员的意义上说,这类似于案例 2。然而,在这种情况下,搜索过程的结果为 0。这是因为在尝试二分搜索之前,该函数确定了它应该搜索的整数索引范围。第一个索引处存在 nil 值意味着确定范围的循环不 运行。有关该搜索过程如何工作的更多信息,您可以查看函数 here。当我说循环不 运行 时,我特指 while 条件 !ttisnil(luaH_getint(t, j)).

奖励案例:

> t = {}
> t[1] = 1
> t[4] = 4
> print(#t)
1
> g = {}
> g[1] = 1
> g[3] = 3
> g[4] = 4
> print(#g)
4

在案例 3 中,我提到了 Lua 决定是将数字索引放在数组部分还是散列部分。上面tablet的组合中,索引1在数组部分,索引4在哈希部分。因此,返回的长度为 1。在 g 的情况下,所有值都放入数组部分。这是因为在为索引赋值时,Lua 尝试以最佳方式重新调整数组部分的大小。为了更好地了解这个重新调整大小的过程,您可以查看来源 here.