2009-09-09 35 views
0

我正在写一个lua脚本,它所做的一件事是将一张表复制到一张表中,并对其应用一些转换。奇怪的是,当我稍后使用这些表格中的一个(并修改它的一些属性)时,更改似乎也会显示在其他表格中!代码:更改一个表似乎改变了另一个

-- thanks to http://stackoverflow.com/questions/1283388/lua-merge-tables/1283608#1283608 
-- tableMerge: 
-- merges two tables, with the data in table 2 overwriting the data in table 1 
function tableMerge(t1, t2) 
    for k,v in pairs(t2) do 
     if type(v) == "table" then 
      if type(t1[k] or false) == "table" then 
       tableMerge(t1[k] or {}, t2[k] or {}) 
      else 
       t1[k] = v 
      end 
     else 
      t1[k] = v 
     end 
    end 
    return t1 
end 

--tableCopy: 
--takes a table and returns a complete copy including subtables. 
function tableCopy(t) 
    return tableMerge({}, t) 
end 

local t1 = { a = 1, b = true, c = "d", e = { f = 2 } } 
local t2 = tableCopy(t1) 
t2.b = false 
t2.e.f = 1 
print(t1.b) -- prints true as it should 
print(t1.e.f) -- prints 1! 

[去除它包含的信息,原因等代码,而这种错误的一个很好的再现]

因此,它是在我的代码或什么错误?我无法弄清楚......

+0

好的,说实话,我在发布答案之前并没有看太多你的代码。对此有罪。但是,我仍然不想深入其中,所以如果您可以编写一小段代码来重现问题,或者至少指出哪些表意外更改,您可能会得到更好的答案。 – sbk 2009-09-09 11:15:23

+0

好的,我会继续前进,并在问题的具体内容上添加一些内容。 – RCIX 2009-09-09 11:34:18

回答

4

这就是Lua表的工作方式 - 它们不会被复制,只会将表的引用传递给函数或存储在表中。如果您熟悉.NET术语,则可以说表是“参考类型”。观察:

function modtable(t) 
    t.hello = "world" 
end 

local t = { hello = "no!"; } 
modtable(t) 
print(t.hello) 

这会打印“世界”,因为可修改的函数获取对表的引用而不是副本。当您尝试存储表在另一个表

local t = { hello = "no!"; } 
local bigT = { innerTable = t; } 
bigT.innerTable.hello = "world" 
print(t.hello) 

t.hello = "double world"; 
print(bigT.innerTable.hello); 

这将打印

world 
double world 

,因为T和bigT.innerTable基本上是相同的表同样的事情发生。

如果你想,你可以独立地修改表的副本之一另一个你可以写一个小功能来复制表

function deep_copy_table(t) 
    local result = {} 
    for k,v in pairs(t) 
    do 
     if (type(v) == "table") 
     then 
      result[k] = deep_copy_table(v) 
     else 
      result[k] = v 
     end 
    end 
    return result 
end 

local t = { hello = "no!"; } 
local bigT = { innerTable = deep_copy_table(t); } 
bigT.innerTable.hello = "world" 
print(t.hello) 

t.hello = "double world"; 
print(bigT.innerTable.hello); 

这将打印“没有!”和“世界” - t和bigT.innerTable现在是不同的表格。

+0

+1,用于清楚说明和示例。 – RBerteig 2009-09-09 08:30:41

+0

但奇怪的是,在问题区域之前,我明确地创建了一个我正在修改的表的副本。也许这是坏的?顺便说一句,这是我见过的最奇怪的格式约定,与“然后”在下一行...... – RCIX 2009-09-09 09:13:43

0

好的,对于双重答案抱歉 - 先前的答案仍然很好,但它是对不同问题的答案。

我在你的代码发现了问题,它的tableMerge功能预期:

if type(v) == "table" then 
      if type(t1[k] or false) == "table" then 
        tableMerge(t1[k] or {}, t2[k] or {}) 
      else 
        t1[k] = v -- this is the problematic line 
      end 
    else 

所以,如果T1 [K]不是表,你只需指定v它和你最后提及v而不是副本。这有效地使你的tableCopy函数浅拷贝而不是深拷贝。既然要覆盖T1 [K]无论如何,这似乎是一个很好的实现tableMerge功能:

function tableMerge(t1, t2) 
    for k,v in pairs(t2) do 
     if type(v) == "table" then 
      if type(t1[k]) ~= "table" then -- if t1[k] is not a table, make it one 
       t1[k] = {} 
      end 
      tableMerge(t1[k], t2[k]) 
     else 
      t1[k] = v 
     end 
    end 
    return t1 
end 

这应该手头希望解决的问题。

其他一些在你的代码的随机的想法,如果你不介意:

  • 原实里mergeTable的:type(t1[k] or false)。该t1[k] or false弄虚作假是没有意义在这里,类型()可以处理零值就好了(这将返回“零”为

  • T2 [K]永远不能。 Lua中的表不能包含nil作为值,而pairs()永远不会返回一个具有nil作为键或值的对。 t2[k] or false再次是毫无意义的。

  • 我在第一个脚本的末尾发现了这行:table.remove(v.Weapons)。我猜你需要的第二个参数调用table.remove

哦,和建议的最后一个部分:不只是降200行代码,并期望人们调试它给你 - 没有一会麻烦。尝试缩小有问题的代码。你已经怀疑tableMerge函数,如果你自己拿出它并对它进行简单的测试,你会发现问题。

快乐编码!

+0

有人建议,旧的代码中有错误,不经常检查类型,我想我太安全了。我原本做了一个测试,并没有显示错误(我没有怀疑只有子表没有被正确复制),但是我意识到刚才的错误,我换出了代码(现在固定为显示bug)测试用例。如果传递table.remove只有一个参数,它将删除表中最后一个索引处的项目。谢谢 – RCIX 2009-09-10 00:54:14

相关问题