自动表

lua-users home
wiki

基于在 FuncTables 中展示的一些技术,这里有一些 Perl 风格的 [1] 自动表(也称为自动生成)的实现。自动表按需创建子表,例如:

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


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 GMT (差异)