如何按值复制 Lua 表?

最近我写了一些像这样的 Lua 代码:

local a = {}
for i = 1, n do
   local copy = a
   -- alter the values in the copy
end

显然,这不是我想做的,因为在 Lua 中变量保存对匿名表的引用,而不是表本身的值。这在《Lua 编程》中已经明确阐述,但我忘了这一点。

因此,问题是我应该写什么来代替 copy = a 以获得 a 值的副本?

原文链接 https://stackoverflow.com/questions/640642

点赞
stackoverflow用户1438
stackoverflow用户1438

这是我实际上做的:

for j,x in ipairs(a) do copy[j] = x end

Doub所提到,如果你的表键不严格单调递增,应该使用pairs而不是ipairs

我还发现了一个更健壮的深层拷贝函数:

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 -- 数字,字符串,布尔值等
        copy = orig
    end
    return copy
end

它通过递归调用本身来处理表和元表(这是它自己的报酬)。其中一个聪明的地方是你可以传递任何值(无论是表还是其他值),它都能正确地进行复制。然而,代价是它可能会潜在地溢出堆栈。因此,可能需要更健壮的(非递归)函数

但是对于仅仅想要将一个数组复制到另一个变量的非常简单的情况来说,这是过度设计了。

2009-03-12 21:52:19
stackoverflow用户48940
stackoverflow用户48940

这就是你在基本表格中能得到的最好的了。如果你需要复制带有元表的表格,可以使用类似 deepcopy 的东西。

2009-03-13 00:48:21
stackoverflow用户67432
stackoverflow用户67432

表格复制有许多潜在的定义。这取决于您是否需要简单或深度复制,是否需要复制、共享或忽略元表等。没有单个实现可以满足所有人。

一种方法是简单地创建一个新表格并复制所有键/值对:

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 仅迭代表格键的子集(即从一开始的连续正整数键按递增顺序)。

2009-03-13 09:51:30
stackoverflow用户68204
stackoverflow用户68204

(不幸的是,文档相对较少的)stdlib项目拥有很多有价值的扩展,适用于标准Lua库的多个类别。其中有数个变体是关于表复制和合并的主题。

这个库也包含在Windows版Lua中,应该成为任何使用Lua时的基本工具箱的一部分。

当手动实现类似的功能时,确保正确处理元表是非常重要的。对于简单的表式结构应用程序来说,你可能没有任何元表,使用pairs()简单循环是一个可接受的答案。但如果该表被用作树状结构,或包含循环引用,或拥有元表,则情况变得更为复杂。

2009-03-13 21:19:37
stackoverflow用户14455
stackoverflow用户14455

我认为 Lua 没有在其标准库中提供“table.copy()”的原因是因为该任务不容易精确定义。正如此处已经展示的,人们可以进行“一级深度”的复制(你已经做到了),深度复制,是否考虑可能的重复引用,还有元表。

就个人而言,我仍然希望它们能提供一个内置函数。只有当人们对其语义感到不满意时,他们才需要自己去实现它。然而,实际上很少遇到需要进行按值复制的情况。


2009-03-15 23:05:28
stackoverflow用户41661
stackoverflow用户41661

为了说明这一点,我个人的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

没有一种复制函数被广泛认可为"标准"。

2009-03-20 00:56:28
stackoverflow用户714389
stackoverflow用户714389

一个可选的深度、图形通用、递归版本:

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

也许metatable的复制也应该是可选的呢?

2011-04-19 01:17:52
stackoverflow用户1442917
stackoverflow用户1442917

在大多数需要复制表格的情况下,我希望得到一个与原始表格没有任何关联的副本,以便对原始表格的任何修改都不会对副本产生影响(反之亦然)。

迄今为止展示的所有代码片段都无法为可能具有共享键或与表相同的键的表格创建一个副本,因为这些键将指向原始表格。如果尝试复制创建为a={}; a[a] = a的表格,则可以轻松地看到这一点。 Jon 引用的 deepcopy 函数将处理此问题,因此,如果需要创建一个真实/完整的副本,应使用 deepcopy

2012-06-10 22:20:40
stackoverflow用户2294006
stackoverflow用户2294006

完整版本的深拷贝,处理以下三种情况:

  1. 表循环引用
  2. 键也是表的情况
  3. 元表

通用版本:

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/CopyTableAlan Yates 的函数。

2013-04-18 08:07:57
stackoverflow用户1917534
stackoverflow用户1917534

不要忘记函数也是引用,因此,如果您想要完全“复制”所有值,则还需要获取单独的函数;然而,我所知道的唯一复制函数的方法是使用 loadstring(string.dump(func)),但是根据 Lua 参考手册,该方法不能用于带有上值的函数。

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))
                -- 如果该函数带有上值,则可能会出现问题。
            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
2013-06-26 15:25:05
stackoverflow用户510121
stackoverflow用户510121

警告:标记的解决方案是错误的

当表格包含表格时,仍将使用对这些表格的引用。我已经搜索了两个小时的错误,而这是因为使用了上面的代码。

因此,您需要检查值是否为表格。如果是,您应该递归调用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

注意:当表格包含函数或其他特殊类型时,这也可能是不完整的,但这是大多数人不需要的可能性。上面的代码对于需要它的人来说很容易适应。

2013-09-24 07:55:10
stackoverflow用户3561
stackoverflow用户3561

为了进行一些易读代码的高尔夫小游戏,这里提供了一个短版本来处理标准的棘手情况:

  • 将表作为键
  • 保留元表
  • 递归表

我们可以用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深度拷贝操作,这里有一个简短的描述:这里的gist

另一个有用的参考资料是Lua-users wiki页面,其中包含避免使用__pairs元方法的示例。

2014-10-14 17:49:10
stackoverflow用户4684797
stackoverflow用户4684797

这可能是最简单的方法:

local data = {DIN1 = "Input(z)", DIN2 = "Input(y)", AINA1 = "Input(x)"}

function table.copy(mytable)  --mytable = 需要复制的表格

    newtable = {}

    for k,v in pairs(mytable) do
        newtable[k] = v
    end
    return newtable
end

new_table = table.copy(data)  -- 复制表格 "data"
2015-06-22 14:24:09
stackoverflow用户1464263
stackoverflow用户1464263

在此处使用 Penlight 库:

https://stevedonovan.github.io/Penlight/api/libraries/pl.tablex.html#deepcopy

local pl = require 'pl.import_into'()
local newTable = pl.tablex.deepcopy(oldTable)
2016-01-31 18:29:53
stackoverflow用户2485995
stackoverflow用户2485995

在我的情况下,当表格中的信息仅为数据和其他表格(不包括函数等)时,以下代码行是最优解:

local copyOfTable = json.decode( json.encode( sourceTable ) )

我正在为 Fibaro Home Center 2 上的一些家庭自动化编写 Lua 代码。Lua 的实现非常有限,没有可以引用的中央函数库。每个函数都需要在代码中声明,因此像这样的一行解决方案是可取的,以保持代码的可维护性。

2016-10-02 23:31:10
stackoverflow用户12968803
stackoverflow用户12968803

只需使用

local unpack = unpack or table.unpack

list2 = {unpack(list)}

即可。

2022-06-23 21:29:04