如何在Lua中获取实际表的副本?

4

因此,以下变量都指向同一张表:

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很难真正按值复制一个变量而不仅仅是按引用复制它?

2个回答

5
为什么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,以确保您自己花时间去弄清楚您的复制算法需要多复杂。


3

通过将值分配给一个新变量,可以简单地复制数字或字符串。然而,要复制一个表格则需要更多的工作。

要在Lua中复制一个表格,您需要定义一个复制函数。常见的两种复制函数类型是浅拷贝和深拷贝。

Lua用户:CopyTable

浅拷贝:

这是一种简单、天真的实现方式。它只复制顶层值及其直接子级;没有处理更深层次的子级,元表或特殊类型,如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)}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接