

Sweet Snippet 之 Lua readonly table
source link: https://blog.csdn.net/tkokof1/article/details/112850563
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Sweet Snippet 之 Lua readonly table
Lua table 用作静态配置是常见的使用情境,而用作静态配置的 Lua table 往往都有保持只读的需求,本文简单介绍了一些让 Lua table 变更为只读的知识 (代码基于 Lua 5.4)
基础变更 Lua table 为只读的方法,在 《Programming in Lua》 中就已经给出了(这里),基本思路即是通过 __index 和 __newindex 两个元方法来做 table 的读写限制,代码大体如下:
function readonly(t)
local proxy = {}
local mt =
{
__index = t,
__newindex = function()
error("attempt to update a readonly table", 2)
end
}
setmetatable(proxy, mt)
return proxy
end
简单测试一下:
local r_t = readonly({ 1, 2, 3 })
print(r_t[1])
-- error here : attempt to update a readonly table
r_t[1] = 2
上述的示例代码中,虽然我们已经让 table 变为了只读,但是获取 table 长度(#)或者使用 pairs 遍历 table 时都不能得到正确结果(使用 ipairs 可以得到正确结果):
local r_t = readonly({ 1, 2, 3 })
-- correct
for k, v in ipairs(r_t) do
print(tostring(k) .. " = " .. tostring(v))
end
-- error
print(#r_t)
-- error
for k, v in pairs(r_t) do
print(tostring(k) .. " = " .. tostring(v))
end
完善的方法也很简单,添加相应的 __len 和 __pairs 元方法即可:
function readonly(t)
local proxy = {}
local mt =
{
__index = t,
__newindex = function()
error("attempt to update a readonly table", 2)
end,
__len = function()
return #t
end,
__pairs = function()
return next, t, nil
end
}
setmetatable(proxy, mt)
return proxy
end
上面的示例代码中仍然存在一个比较大的问题:如果 table 中存在另外的 table
元素,经过上述 readonly 函数处理之后,这些 table 子元素仍然不是只读的:
local r_t = readonly({ 1, 2, 3, {} })
r_t[1] = 1 -- error
r_t[4] = {} -- error
r_t[4][1] = 1 -- correct ...
为了解决这个问题,我们需要递归的对 table 做 readonly 操作,相关代码如下:
local proxies = {}
function readonly(t)
if type(t) == "table" then
local proxy = proxies[t]
if not proxy then
proxy = {}
local mt =
{
__index = function(_, k)
return readonly(t[k])
end,
__newindex = function()
error("attempt to update a readonly table", 2)
end,
__len = function()
return #t
end,
__pairs = function()
local function readonly_next(t, i)
local n_i, n_v = next(t, i)
return n_i, readonly(n_v)
end
return readonly_next, t, nil
end
}
setmetatable(proxy, mt)
proxies[t] = proxy
end
return proxy
else
return t
end
end
示例代码并没有对 table 进行全量的只读变更(我们自然也可以这么做),而是在访问 table 元素时以增量方式进行的,这有益于分摊程序消耗.
经过了上面几步, readonly 函数已经几近完善,但仍然存在问题,如果我们使用 rawset(类似的还有 rawget) 绕过元方法来设置 table,那么 table 仍然会被更新(而不能做到只读):
local r_t = readonly({ 1, 2, 3, {} })
rawset(r_t, 1, 2) -- correct ...
如果需要解决这个问题,目前就需要在宿主语言侧(譬如 C)来实现只读的 table 类型,并导出给 Lua 来使用.
Recommend
-
28
Sweet Snippet 之 BitMask
-
12
Sweet Snippet 之 Timeslice Update
-
24
Sweet Snippet 之 方差计算
-
14
Sweet Snippet 之 字符串编辑距离
-
14
Sweet Snippet 之 Gram-Schmidt 正交化
-
13
Sweet Snippet 之 Lua Utils_tkokof1的专栏-CSDN博客 Sweet Snippet 之 Lua Utils ...
-
14
Sweet Snippet 系列之 埃拉托斯特尼(Eratosthenes)筛法 ...
-
9
Sweet Snippet 之矩阵求逆
-
10
Sweet Snippet 系列之 扩展欧几里得算法
-
10
Sweet Snippet 系列之 有序列表
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK