因此,以下变量都指向同一张表:
x = {1,2,3}
y=x
z=y
table.remove(z,3)
因此,以下代码将输出1,2。
for k,v in pairs(x) do
print(v)
end
互联网只是指Lua始终通过引用而不是值使用变量的能力。
但有时我想操作变量的副本,而不是原始变量。如何做到这一点?为什么Lua很难真正按值复制一个变量而不仅仅是按引用复制它?
local tbl = {x = 5, y = {20}}
y
字段拥有旧表存储的表的副本?还是您希望该表本身成为原始表的副本?local tbl = {x = 5, y = {20}}
tbl._tbl = tbl
这个表现在存储了一个对自身的引用。试图盲目递归地复制该表将导致无限递归。你需要检测到表引用自身,因此新表必须存储对新表的引用。而且情况变得更加复杂:
local tbl = {x = 5, y = {20}}
tbl.z = tbl.y
这个表现在有两个字段引用同一个表。如果你想要一个真正的该表副本,那么副本需要意识到两个字段相互引用,这样当它复制第一个字段时,它可以使第二个字段引用新的副本而不是再次复制。
我甚至还没有讨论过元表和你可以进行的操作。这也没有包括像来自基于C的API的userdata对象这样的基本不可复制的事物的讨论。如果我将io.open
的结果存储在一个表中,那么就没有机制来复制那个文件句柄。那么你的复制例程应该做什么呢?
Lua没有默认的表复制API,以确保您自己花时间去弄清楚您的复制算法需要多复杂。
通过将值分配给一个新变量,可以简单地复制数字或字符串。然而,要复制一个表格则需要更多的工作。
要在Lua中复制一个表格,您需要定义一个复制函数。常见的两种复制函数类型是浅拷贝和深拷贝。
浅拷贝:
这是一种简单、天真的实现方式。它只复制顶层值及其直接子级;没有处理更深层次的子级,元表或特殊类型,如userdata或coroutines。它也容易受到__pairs元方法的影响。
function shallowcopy(orig)
local orig_type = type(orig)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in pairs(orig) do
copy[orig_key] = orig_value
end
else -- number, string, boolean, etc
copy = orig
end
return copy
end
深度复制:
深度复制会复制所有层级(或指定的一部分层级)。以下是一个简单的递归实现,还能处理元表并避免使用__pairs元方法。
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
正如尼古拉·波拉斯所说,复制表格存在许多陷阱。在另一个SO问题how-do-you-copy-a-lua-table-by-value中,给出了以下示例,其中涵盖了一些值得关注的情况,例如:
Tyler 的示例:
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
这些函数各有不同的用途,如果您正在处理像 x = {1,2,3}
这样的浅表,可以使用以下简单方法:
x = {1,2,3}
y = {table.unpack(x)}