复制表

lua-users home
wiki

Lua 标准库没有提供复制表的函数。但是,实现这样的函数相对简单。

通用的 table.copy 函数不能保证适合所有用例,因为有许多不同的方面必须针对特定情况进行选择。例如:元表应该共享还是复制?我们应该检查用户数据是否有 __copy 元方法?这些问题(以及许多其他问题)必须由开发人员提出并回答。

一些 Lua “扩展标准库”,如 Penlight 和 stdlib 提供了现成的复制函数以方便使用。在实现自己的函数之前,请检查它们是否适合您的用例。

以下函数提供了一个基础来进行工作

快速且肮脏

function table.clone(org)
  return {table.unpack(org)}
end

local abc = {5,12,1}
local def = table.clone(abc)
table.sort(def)
print(abc[2], def[2]) -- 12	5

浅拷贝

这是一个简单、朴素的实现。它只复制顶层值及其直接子项;没有处理更深层的子项、元表或特殊类型,如用户数据或协程。它也容易受到 __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

通过对代码进行一些细微的修改,也可以创建一个适用于递归表的 deepcopy 函数版本。这是通过创建一个已复制表的表,并将它作为第二个参数传递给 deepcopy 函数来实现的。

-- Save copied tables in `copies`, indexed by original table.
function deepcopy(orig, copies)
    copies = copies or {}
    local orig_type = type(orig)
    local copy
    if orig_type == 'table' then
        if copies[orig] then
            copy = copies[orig]
        else
            copy = {}
            copies[orig] = copy
            for orig_key, orig_value in next, orig, nil do
                copy[deepcopy(orig_key, copies)] = deepcopy(orig_value, copies)
            end
            setmetatable(copy, deepcopy(getmetatable(orig), copies))
        end
    else -- number, string, boolean, etc
        copy = orig
    end
    return copy
end

重要的是,只向此版本的 deepcopy 函数提供一个参数。否则,它将尝试使用第二个参数作为表,这可能会产生意想不到的后果。

此外,由于这些函数是递归的,因此使用它们来复制非常深的表可能会导致堆栈溢出。

非递归深拷贝

一个更灵活的(非递归)深拷贝实现可从 [此 GitHub gist] 获取。它允许对如何复制特殊类型、元表和函数上值(包括合并)制定不同的规则。请参阅注释以了解用法。
最近更改 · 偏好设置
编辑 · 历史
最后编辑于 2022 年 12 月 6 日上午 10:07 GMT (差异)