Lua 全局 __newindex 只调用了一次

Lua global __newindex called only once

在 7 周内用 7 种以上的语言完成 Lua 练习并陷入元table 问题。

挑战在于重载 + 运算符以便能够连接 table(就像它们是数组一样)。

{ 1, 2, 3 } + { 4, 5, 6 } -- { 1, 2, 3, 4, 5, 6 }

所以我尝试使用 __add 元方法并创建了一个元table 来托管它。

local mt = {
  __add = function(lhs, rhs)
    return concatenate(lhs, rhs)
  done
}

我错误地尝试在全局 table 上设置此元 table,但这显然不会传播到所有其他 table。

为了将它添加到所有已创建的 table,我在 _G 上创建了一个单独的元 table,它将使用 __newindex 元方法,然后设置原始元table 每 __newindex 被激活。

setmetatable(_G, {
  __newindex = function(array, index)
    setmetatable(array, mt)
  end
})

然而,这没有用:

a1 = { 1, 2, 3 }
a2 = { 4, 5, 6 }
a1 + a2

并导致以下错误:attempt to perform arithmetic on global 'a1' (a nil value)

所以我在全局元table中扔了一条打印语句,看看它是否真的被调用了:

setmetatable(_G, {
  __newindex = function(array, index)
    print('new table ' .. index)
    setmetatable(array, mt)
  end
})

这只会打印要创建的第一个 table:

a1 = { 1, 2, 3 }
a2 = { 4, 5, 6 }
a3 = { 4, 5, 6 }
a4 = { 4, 5, 6 }

结果:

new table a1

我预计我不小心覆盖了某些东西,因为当我删除对 setmetatable

的调用时
setmetatable(_G, {
  __newindex = function(array, index)
    print('new table ' .. index)
    --setmetatable(array, mt)
  end
})

它按预期打印所有条目。

setmetatable(_G, {
    __newindex = function(array, index)
        print('new table ' .. index)
        setmetatable(array, mt)
    end
})

当您执行 a1 = { 1, 2, 3 } 时,将调用 __newindex,其中 array_Gindex 为 'a1'。然后调用 setmetatable(array, mt) 将更改 _G 的元 table,取消原始 setmetatable 调用的效果。

您可能想要更像:

setmetatable(_G, {
    __newindex = function(array, index)
        setmetatable(array[index], mt)
    end
})

但是还有另一个问题,因为现在分配的原始效果不再发生。 __newindex 被调用,所以 a1 仍然是一个零值。

您可以在 __newindex 函数中尝试 array[index] = value,但这会再次调用相同的 __newindex。相反,使用 rawset:

setmetatable(_G, {
  __newindex = function(array, index, value)
    rawset(array, index, value)
    setmetatable(array[index], mt)
  end
})

请注意 __newindex 有 3 个参数。

现在,当您将非 table 分配给 b = 1 这样的全局变量时会发生什么?