重新初始化表而不会丢失引用

时间:2017-03-07 08:59:36

标签: lua

我想重新初始化一个表而不会丢失对它的引用。

我想要实现的是在文件中定义表格,并且当文件被更改时(使用文本编辑器)重新加载文件,更改表格。当然,这不会改变表格但会创建一个新实例,旧的引用仍将指向旧表。

有什么建议吗?

编辑:我想详细说明我想要实现的目标。游戏角色和武器的一个例子。我想修改weapon.lua,以便影响角色。

-- weapons.lua
sword = { damage = 3 }

-- characters.lua
character = { weapon = sword }

除非我将角色分成{weaponTable = weapon,weaponKey,否则像JWT建议的那样添加一个间接级别(把#34;剑"内部"武器")都没有帮助="剑"但我不认为这是一种选择。

3 个答案:

答案 0 :(得分:2)

在全球环境中锚定 一切需要生存的。嵌套很好,这不一定是你的主要参考。 (您仍然可以local件事,但请确保从全局环境初始化这些局部变量,并在更改本地时更新全局变量。)

要初始化全局值,请说

foo = foo or value                  -- if foo is always true-ish
bar = (bar == nil) and value or bar -- if bar may be `false`

要初始化或更新表格,您可以

foo = foo or { }
foo.bar = foo.bar or 23
foo.baz = foo.baz or 42
-- and so on...

但那有点蠢,所以也许可以说

function reinit( new, old ) -- (re)initialize one level, prefer old
  if old == nil then  return new  end
  if type( old ) ~= "table" then  return old  end
  for k, v in pairs( new ) do
    if old[k] == nil then  old[k] = v  end
  end
  return old
end
function reset( new, old ) -- (re)initialize one level, prefer new
  if old == nil then  return new  end
  if type( old ) ~= "table" then  return new  end
  for k, v in pairs( new ) do  old[k] = v  end
  return old
end

然后只是

foo = reinit( { bar = 23, baz = 42 }, foo ) -- only setting if not defined
-- or
foo = reset( { bar = 23, baz = 42 }, foo ) -- always overriding given fields

或者可能让它变得更加花哨并说出

function traverse( t, path )
  local here, last, lastk, created = t
  -- follow path of several keys starting from t, creating tables as needed
  for k in path:gmatch "[^.]+" do
    k = tonumber( k ) or k -- convert all-number keys to integer (for arrays)
    local next = here[k]
    if not next then
      next, created = { }, true
      here[k] = next
    else
      created = false
    end
    lastk, last, here = k, here, next
  end
  return here, last, lastk, created
end
function repopulate( path, value, update )
  update = update or reinit -- pass 'reset' as 'update' for the other behavior
                       -- or something entirely different if that's what you need
  local here, last, lastk, created = traverse( _G, path )
  if type( value ) == "table" then
    update( value, here )
  else
    if created then  last[lastk] = nil  end -- created one level too much
    update( { [lastk] = value }, last )
  end
end

然后(使用任意嵌套)

-- No need to create 'state' first if it doesn't exist yet!
-- (If it exists, it will get updated, otherwise it's created)
repopulate( "state.player.weapon", { kind = "sword", damage = 11 } )
-- Do keep in mind that generally update order is relevant -- you may want to
-- give a newly created player a default inventory, but you may not want to
-- "refill" the player's inventory on every reload.  So generally `repopulate`
-- with the parent and all child nodes for from-scratch creation, then
-- `repopulate` the child nodes that need to be checked & potentially updated
-- as well.
-- (So here you'd probably repopulate `state.player` first and then 
-- `state.player.weapon` or other fields only if they should be updated anyway.)
-- e.g.:
repopulate( "state.player", {
  x = 0, y = 0, hp = 100, mp = 100, level = 0, -- and so on
  weapon = { kind = "sword", damage = 11 }, -- etc. etc.
} )
-- on reload always force a sword-kind weapon, leave rest (damage, ...) as-is
repopulate( "state.player.weapon", { kind = "sword" }, reset )
-- or alternatively: only if player has no weapon, give them a sword
repopulate( "state.player.weapon", { kind = "sword", damage = 3 } )

你可以更进一步,添加元方法来隐藏一些改组,定义不同的更新策略,... - 你已经看到了一些可能性,现在去构建你自己的版本适合您的风格和代码

(虽然您可以以任何方式自由使用上述代码,但请注意它是在浏览器中临时编写的。我做了一些测试,修复了一些故障,似乎< / em>现在正在工作,但如果还有一两个漏洞隐藏在那里也不要感到惊讶。所以玩这个,改变它,打破它(看看它是如何/为什么打破),适应和扩展它,。 .. - 但除非你完全了解它的作用并且可以修复任何错误,我强烈建议你编写自己的版本,或者只是坚持基础。你可能不需要这样做的一切,你可能还需要其他不做的事情。因为这是重新加载/实时编码基础设施的核心部分,所有内容都必须适应重载兼容,你的工具和工具之间的任何不匹配你实际上需要在你的代码中导致很多很多。所以如果你需要这样的东西,可以用一两天时间让它按照你的方式工作需要它,或者你会后悔的。)

(免费奖励警告:如果你做OOP,你可能不得不存储和检索你的类而不是每次创建它们,否则以前迭代的旧对象将错过代码更新并仍然运行他们的旧方法我已经忘记了不止这几次而浪费了几个小时思考“为什么现在不修复?!?”反复重新加载代码之后...所以记得锚定你的元数据,锚定你的课程!)功能

答案 1 :(得分:1)

您可以将表嵌套在另一个表中。

在:

local a = { 1, 2, 3 }
local b = { 7, 8, 9 }
print(a[2] + b[2])  -- #=> 10

后:

local lookup = {
  a = { 1, 2, 3 },
  b = { 7, 8, 9 }
}
print(lookup.a[2] + lookup.b[2])  -- #=> 10

然后,您可以完全替换(或只更新)查找表中的表,任何依赖语句都将使用该更新值:

lookup.a = { 100, 50, 0 }
print(lookup.a[2] + lookup.b[2])  -- #=> 58

答案 2 :(得分:1)

我不知道它是否正是您所需要的(因为ID是必要的)但我希望它能满足您的需求。

meta = {
  tables = {},
  __call = function(arg, t)
    for k, v in pairs(t) do
      arg[k] = v
    end
  end, 

  __bnot = function(arg) 
    return arg.__key
  end,

  __newindex = function(arg, key, val) 
    meta.tables[arg.__key][key] = val 
  end,

  __index = function(arg, key) 
    return meta.tables[arg.__key][key] 
  end
}

function RefTable(arg)
  local newtable = {}

  if arg ~= nil then
    newtable.__key = arg
    setmetatable(newtable, meta)

    if meta.tables[arg] == nil then
      meta.tables[arg] = {}
    end
  else
    error("RefTable can't have nil key")
  end

  return newtable
end

-- Using the RefTable
sword = RefTable("Sword")
sword({damage = 3})
sword.cooldown = 10
character = {sword = sword}

print("base", sword.damage, sword.cooldown)
print("embed", character.sword.damage, character.sword.cooldown)

sword = RefTable("Sword")
sword({damage = 8, cooldown = 50})

print("embed2", character.sword.damage, character.sword.cooldown)
print(sword.__key, sword.cooldown)
ref = RefTable("Sword")
ref.cooldown = 1000
print(sword.cooldown)