lua onlyread table 的实现 与遍历

只读表的实现

游戏开发中有时候我们需要实现一个table初始化过后,就只能访问其值却不能修改其内容,采用代理思想很容易实现一个只读table,我们需要做得只是当我们监控到企图修改表时候抛出错误。通过__index metamethod,我们可以不使用函数而是用原始表本身来使用表,因为我们不需要监控查寻。这是比较简单并且高效的重定向所有查询到原始表的方法。但是,这种用法要求每一个只读代理有一个单独的新的metatable,使用__index指向原始表;lua程序设计给出了实现:

[lua]
function readOnly (t)
local proxy = {}
local mt = { — create metatable
__index = t,
__newindex = function (t,k,v)
error("attempt to update a read-only table", 2)
end
}

setmetatable(proxy, mt)
return proxy
end
[/lua]

注:(error的第二个参数2,将错误信息返回给企图执行update的地方)

下面我们测试一下我们的代码:

[lua]
days = readOnly{"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"}

print(days[1]) –> Sunday
days[2] = "Noday"
[/lua]

 

输出:

Sunday
lua: readTest.lua:18: attempt to update a read-only table
stack traceback:
[C]: in function ‘error’
readTest.lua:6: in function <readTest.lua:5>
readTest.lua:18: in main chunk
[C]: ?

我们看到,当我们试图去修改只读表时就会抛出错误

但是这样处理后的table我们的迭代器都将失效(pairs, ipairs, next

[lua]
days = readOnly{"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"}

print(days[1]) –> Sunday
–days[2] = "Noday"
— prints nothing!
for k,v in pairs(days) do
print(k,v)
end

print(next(days)) — prints nil!
print(#days) — prints "0"!
[/lua]

输出:

Sunday
nil
0

可以看到我们现在无法遍历只读表的元素了接下来我没就来讨论怎么去遍历我们只读表

如何遍历只读表

我们想重新遍历只读表,只能重写next()方法了 ,重新用心的next()定义pairs()还要的对readOnly方法做点小的修改,下面改成全部代码:

[lua]
rawnext = next
function next(t,k) –重定义next
local m = getmetatable(t)
local n = m and m.__next or rawnext –添加__next
return n(t,k)
end
function readOnly (t)
local proxy = {}
local mt = { — create metatable
__index = t,
__newindex = function (t,k,v)
error("attempt to update a read-only table", 2)
end,
__next = function(tt, k) –添加__next元表实现
return next(t, k)
end
}

setmetatable(proxy, mt)
return proxy
end
days = readOnly{Sunday = 1, Monday = 2, Tuesday = 3, Wednesday = 4,
Thursday = 5, Friday = 6, Saturday = 7}

print(days["Sunday"]) –> 1
–days["Tuesday"] = 3

function pair (t)
return next, t, nil
end
function pairs(t) return next, t, nil end –重定义pairs
–for k,v in next, days do print(k,v) end
for k ,v in pairs(days) do

print(k..v)
end
[/lua]

输出:

1
Saturday7
Tuesday3
Wednesday4
Friday6
Sunday1
Thursday5
Monday2

可以看到我么可以用pairs()重新遍历我们的只读表了

更多参考Generalized Pairs And Ipairs