当前位置:  开发笔记 > 编程语言 > 正文

如何按值复制Lua表?

如何解决《如何按值复制Lua表?》经验,为你挑选了6个好方法。

最近我写了一些Lua代码,如:

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

显然,这不是我想要做的,因为变量持有对匿名表的引用而不是Lua中表本身的值.Lua中的编程清楚地阐述了这一点,但我忘了它.

所以问题是我应该写什么而不是copy = a获取值的副本a



1> Doub..:

表副本有许多潜在的定义.这取决于您是想要简单还是深度复制,是否要复制,共享或忽略元数据等.没有单一的实现可以满足每个人.

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

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只迭代表键的一个子集(即,以递增的顺序从一开始的连续正整数键).


代码不正确,如果您不需要递归复制,这是复制表的完美有效方法.如果需要递归复制,则使函数递归.它没有包含在标准库中的原因是没有"表副本"的单一定义.
我可以看到@scippie点.也许所需要的是一个更好地表明正在进行的复制类型的名称?我将名称更新为`shallow_copy`; 如果有任何分歧,请告诉我.

2> Norman Ramse..:

为了说明这一点,我的个人table.copy也关注metatables:

function table.copy(t)
  local u = { }
  for k, v in pairs(t) do u[k] = v end
  return setmetatable(u, getmetatable(t))
end

没有足够广泛认同的复制功能被称为"标准".


请注意,更改或扩展标准命名空间(如`table`)被认为是一种不好的做法.
这有效,直到有人设置metatable的`__metatable`属性.

3> Tyler..:

要玩一点可读代码高尔夫,这是一个处理标准棘手案例的简短版本:

表作为键,

保留metatables,和

递归表.

我们可以用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-users wiki页面,其中包含一个如何避免__pairsmetamethod的示例.



4> 小智..:

完整版的深层复制,处理所有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/CopyTable和Alan Yates的功能.


哈,希望自己在写这篇文章之前进一步向下滚动!(IMO这应该是公认的答案.)

5> 小智..:

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

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副本也应该是可选的?


你需要在迭代之前将`nt`添加到`seen`,或者在某些情况下你会溢出堆栈.基本上,删除15,并在第6行后插入.

6> Jon Ericson..:

这是我实际做的:

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

作为DOUB提到,如果表项不严格单调递增的,它应该是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

它通过递归调用自身来处理表和元表(这是它自己的奖励).其中一个聪明的部分是你可以传递任何值(无论是否是表),它将被正确复制.但是,成本是它可能会溢出堆栈.因此,可能需要更强大(非递归)的功能.

但是对于想要将数组复制到另一个变量的非常简单的情况来说,这太过分了.

推荐阅读
我我檬檬我我186
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有