2009-09-09 38 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

相關問題