自动表 |
|
a = AutomagicTable()
a.b.c.d = "a.b and a.b.c are automatically created"
这个版本是 ThomasWrensch 和我 (RiciLake) 之间几次有趣交流的结果,我不能为此认领功劳(尽管我很乐意承担责任;我已经做了一些修改并更改了格式,因为我喜欢参数列表中的空格)
do local auto, assign function auto(tab, key) return setmetatable({}, { __index = auto, __newindex = assign, parent = tab, key = key }) end local meta = {__index = auto} -- The if statement below prevents the table from being -- created if the value assigned is nil. This is, I think, -- technically correct but it might be desirable to use -- assignment to nil to force a table into existence. function assign(tab, key, val) -- if val ~= nil then local oldmt = getmetatable(tab) oldmt.parent[oldmt.key] = tab setmetatable(tab, meta) tab[key] = val -- end end function AutomagicTable() return setmetatable({}, meta) end end
我认为上面的技术——使用带有额外非元方法键的个性化元表——不仅仅是一个方便的技巧。
如果你想将一些信息与一个表关联起来,你有三个选择
第一个技巧的问题是它可能与现有键冲突,或者用不合适的数据污染表。你之前在你的消息中指出了第二种方法的一些问题(关于 GarbageCollectingWeakTables)。第三种方法需要使用一个单独的元表,但没有其他两种方法的问题。
另一个实现,具有有限的生成表的深度;
-- index function to do the magic local autotable__index = function(self, key) local mt = getmetatable(self) local t = {} if mt.depth ~= 1 then setmetatable(t, { __index = mt.__index, depth = mt.depth - 1}) end self[key] = t return t end --- Creates a new auto-table. -- @param depth (optional, default 1) how deep to auto-generate tables. The last -- table in the chain generated will itself not be an auto-table. If `depth == 0` then -- there is no limit. -- @return new auto-table function newautotable(depth) return setmetatable({}, {__index = autotable__index, depth = depth }) end --- Checks a table to be an auto-table -- @param t table to check -- @return `true` if `t` is an auto-table, `false` otherwise function isautotable(t) if type(t) ~= "table" then return false end return ((getmetatable(t) or {}).__index == autotable__index) end
以及一些测试来验证它(如何)工作
local deeptest = newautotable(0) local x = deeptest for n = 1, 100 do x = x.a end x.final = "100 levels deep" x = deeptest for n = 1, 100 do x = x.a end print("Created:",x.final) local testtable = newautotable(2) print(testtable.a.b) -- depth == 2, so a and b are autogenerated print(testtable.a.b) -- but b is last level and no longer auto-table itself testtable.a.b.hello = "world" testtable.a.b.world = "hello" print(testtable.a.b.hello) print(testtable.a.b.world) print(isautotable(testtable.a)) print(isautotable(testtable.a.b)) table.insert(testtable.a.b, "hello world 1") table.insert(testtable.a.b, "hello world 2") for k,v in pairs(testtable.a.b) do print(k,v) end print(testtable.a, isautotable(testtable.a)) --> table, true print(testtable.a.b, isautotable(testtable.a.b)) --> table, false print(testtable.a.b.c) --> nil print(testtable.a.b.c.d) --> error