最近我写了一些类似以下的 Lua 代码:
local a = {}
for i = 1, n do
local copy = a
-- alter the values in the copy
end
显然,这不是我想要的,因为在Lua中变量保存的是匿名表的引用而不是表本身的值。这在《Lua编程》明确说明了,但我忘记了这一点。所以问题是,我应该写什么代码来获取a
表中的值的副本,而不是像copy = a
那样获取表的引用?表格复制有许多潜在的定义。这取决于您想要简单还是深度复制,是否想要复制、共享或忽略元表等。没有一种实现可以满足所有人。
一种方法是简单地创建一个新表格并复制所有键/值对:
function table.shallow_copy(t)
local t2 = {}
for k,v in pairs(t) do
t2[k] = v
end
return t2
end
copy = table.shallow_copy(a)
请注意,应使用 pairs
而不是 ipairs
。因为 ipairs
只会迭代表中的一部分键(也就是以1开始的连续正整数键按升序排序)。
只是为了阐明这一点,我的个人 table.copy
也关注元表:
function table.copy(t)
local u = { }
for k, v in pairs(t) do u[k] = v end
return setmetatable(u, getmetatable(t))
end
table
进行更改或扩展被认为是一种不良实践。 - Alexander Gladysh__metatable
属性。 - Ericnext
еҮҪж•°пјҢеӣ дёәpairs
зҡ„иЎҢдёәеҸҜиғҪдјҡеҸ—еҲ°е…ғж–№жі•__pairs
зҡ„еҪұе“ҚгҖӮжӯӨеӨ–пјҢдҪ еҸҜд»ҘйҖҡиҝҮдҪҝз”Ёdebug.getmetatable
жқҘи§ЈеҶі__metatable
й—®йўҳ...дҪҶжҲ‘дёҚзЎ®е®ҡиҝҷж ·еҒҡжҳҜеҗҰеҗҲйҖӮгҖӮ - Deco为了玩一个可读性代码压缩的小游戏,这里提供了一份处理标准棘手情况的简短代码:
我们可以用7行代码来实现:
function copy(obj, seen)
if type(obj) ~= 'table' then return obj end
if seen and seen[obj] then return seen[obj] end
local s = seen or {}
local res = setmetatable({}, getmetatable(obj))
s[obj] = res
for k, v in pairs(obj) do res[copy(k, s)] = copy(v, s) end
return res
end
这个代码片段简短地介绍了Lua深度复制操作。
另一个有用的参考是这个Lua用户维基页面,其中包括一个示例,介绍如何避免__pairs
元方法。
__newindex
可能会做各种有趣的事情)。此外,__pairs
或__index
可能会干扰迭代,因此最好使用for k,v in next,obj,nil do
来复制实际内容(这相当于rawget
用于pairs
),然后在最后设置元表(这将使事情看起来相同)。另外:为什么不将seen = seen or {}
作为第一行(这更可读),然后if seen and seen[x]
--> if seen[x]
? - nobodyseen
行放在第一位的建议很好。我认为我写成现在这样是因为我想保持行数短(因此使用s
作为变量名),但也清楚地说明了该变量的含义;给它两个名称是我的解决方案(这样行数就相同了)。 - Tyler完整的深拷贝版本,处理所有三种情况:
一般版本:
local function deepcopy(o, seen)
seen = seen or {}
if o == nil then return nil end
if seen[o] then return seen[o] end
local no
if type(o) == 'table' then
no = {}
seen[o] = no
for k, v in next, o, nil do
no[deepcopy(k, seen)] = deepcopy(v, seen)
end
setmetatable(no, deepcopy(getmetatable(o), seen))
else -- number, string, boolean, etc
no = o
end
return no
end
或者表格版本:
function table.deepcopy(o, seen)
seen = seen or {}
if o == nil then return nil end
if seen[o] then return seen[o] end
local no = {}
seen[o] = no
setmetatable(no, deepcopy(getmetatable(o), seen))
for k, v in next, o, nil do
k = (type(k) == 'table') and k:deepcopy(seen) or k
v = (type(v) == 'table') and v:deepcopy(seen) or v
no[k] = v
end
return no
end
基于 lua-users.org/wiki/CopyTable 和 Alan Yates 的函数。
seen
变量是一种缓存系统。我得试试。 - Jon Ericson一个可选的深度、图形通用、递归版本:
function table.copy(t, deep, seen)
seen = seen or {}
if t == nil then return nil end
if seen[t] then return seen[t] end
local nt = {}
for k, v in pairs(t) do
if deep and type(v) == 'table' then
nt[k] = table.copy(v, deep, seen)
else
nt[k] = v
end
end
setmetatable(nt, table.copy(getmetatable(t), deep, seen))
seen[t] = nt
return nt
end
nt
添加到 seen
中,否则会出现堆栈溢出的情况。基本上,删除第15行,并在第6行后插入它。 - Mudlocal t = {}; t [1] = t
或其他类似情况,这也是个问题。此外,在查看http://lua-users.org/wiki/CopyTable之后,我发现您忘记复制键,这些键可能是表本身。 - Jasmijn这是我实际所做的:
for j,x in ipairs(a) do copy[j] = x end
pairs
而不是ipairs
。deepcopy
函数:function deepcopy(orig)
local orig_type = type(orig)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in next, orig, nil do
copy[deepcopy(orig_key)] = deepcopy(orig_value)
end
setmetatable(copy, deepcopy(getmetatable(orig)))
else -- number, string, boolean, etc
copy = orig
end
return copy
end
不要忘记函数也是引用,所以如果你想完全“复制”所有的值,你需要获取单独的函数;然而,我所知道的唯一复制函数的方法是使用loadstring(string.dump(func))
,但根据Lua参考手册,这种方法不适用于带有upvalues的函数。
do
local function table_copy (tbl)
local new_tbl = {}
for key,value in pairs(tbl) do
local value_type = type(value)
local new_value
if value_type == "function" then
new_value = loadstring(string.dump(value))
-- Problems may occur if the function has upvalues.
elseif value_type == "table" then
new_value = table_copy(value)
else
new_value = value
end
new_tbl[key] = new_value
end
return new_tbl
end
table.copy = table_copy
end
很遗憾,stdlib 项目的文档相对较少,但它为标准 Lua 发行版附带的多个库提供了许多有价值的扩展。其中包括几个不同主题的表格复制和合并变体。
该库也包含在 Lua for Windows 发行版中,并且可能是任何严肃的 Lua 用户工具箱的一部分。
手动实现此类内容时要确保正确处理元表。对于简单的表格作为结构的应用程序,您可能没有任何元表,使用 pairs()
的简单循环就是可接受的答案。但是,如果表格用作树,或包含循环引用或元表,则情况会变得更加复杂。
警告:标记的解决方案是不正确的!
当表格包含表格时,仍将使用对这些表格的引用。我花了两个小时搜索我所犯的错误,而实际上是因为使用了以上代码。
因此,您需要检查该值是否为表格。如果是,则应递归调用table.copy!
这是正确的table.copy函数:
function table.copy(t)
local t2 = {};
for k,v in pairs(t) do
if type(v) == "table" then
t2[k] = table.copy(v);
else
t2[k] = v;
end
end
return t2;
end