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
为 _G
,index
为 '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
这样的全局变量时会发生什么?
在 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
为 _G
,index
为 '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
这样的全局变量时会发生什么?