自動魔法表

lua-users home
wiki

根據某些在 FuncTables 中說明的技巧,以下是 Perl 式的自動魔法表的實作(也稱為自動實例化):[1]。自動魔法表會根據需求建立子表,也就是說;

a = AutomagicTable()
a.b.c.d = "a.b and a.b.c are automatically created"

這個版本是我和 ThomasWrenschRiciLake)之間幾次有趣的交流的結果,我不能居功(雖然我很樂意代罪;我做了一些修改並更改了格式,因為我喜歡參數清單有空格)

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


我引用了 ThomasWrensch 的評論,未經許可,希望他不介意

我想上述技巧(使用具有額外非元方法金鑰的個別元表)不只是一項便利的技巧。

如果您想將一些資訊與表關聯,您有三個選項:

1. 在表中放入一個包含資訊的鍵。
2. 將表當作另一個(可能是弱的)表中的鍵。
3. 將其與元表關聯。

第一個技巧的問題是它可能會與現有鍵發生衝突和/或用不適當的資料污染表格。您在 GarbageCollectingWeakTables 中的最後一則訊息指出第二種方法的一些問題。第三種方法需要使用個別元表,但沒有其他兩種技巧的問題。

-- ThomasWrensch

另一個實作,生成表的深度有限制;

-- 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

最近變更 · 喜好設定
編輯 · 記錄
最近編輯時間:2016 年 3 月 17 日,下午 1:24(格林威治標準時間),(diff)