如何按值复制 Lua 表?

发布于 2024-07-15 21:51:45 字数 364 浏览 7 评论 0原文

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

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

显然,这不是我想要做的,因为变量保存对匿名表的引用,而不是 Lua 中表本身的值。 Programming in Lua 中清楚地阐述了这一点,但我忘记了。

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

Recently I wrote a bit of Lua code something like:

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

Obviously, that wasn't what I wanted to do since variables hold references to an anonymous table not the values of the table themselves in Lua. This is clearly laid out in Programming in Lua, but I'd forgotten about it.

So the question is what should I write instead of copy = a to get a copy of the values in a?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(16

太阳哥哥 2024-07-22 21:51:45

表副本有许多潜在的定义。 这取决于您想要简单复制还是深层复制,是否想要复制、共享或忽略元表等。没有单一的实现可以满足所有人的要求。

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

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

Table copy has many potential definitions. It depends on whether you want simple or deep copy, whether you want to copy, share or ignore metatables, etc. There is no single implementation that could satisfy everybody.

One approach is to simply create a new table and duplicate all key/value pairs:

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)

Note that you should use pairs instead of ipairs, since ipairs only iterate over a subset of the table keys (ie. consecutive positive integer keys starting at one in increasing order).

披肩女神 2024-07-22 21:51:45

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

没有足够广泛的复制函数被称为“标准”。

Just to illustrate the point, my personal table.copy also pays attention to metatables:

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

There is no copy function sufficiently widely agreed upon to be called "standard".

巾帼英雄 2024-07-22 21:51:45

为了玩一点可读代码高尔夫,这里有一个简短的版本,它处理标准的棘手情况:

  • 表作为键、
  • 保留元表和
  • 递归表。

我们可以用 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

this gist 中有一段关于 Lua 深复制操作的简短描述。

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

To play a little readable-code-golf, here's a short version that handles the standard tricky cases:

  • tables as keys,
  • preserving metatables, and
  • recursive tables.

We can do this in 7 lines:

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

There is a short write-up of Lua deep-copy operations in this gist.

Another useful reference is this Lua-users wiki page, which includes an example on how to avoid the __pairs metamethod.

你的往事 2024-07-22 21:51:45

深度复制的完整版本,处理所有 3 种情况:

  1. 表循环引用
  2. Key 也是表
  3. Metatable

通用版本:

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' 功能。

The full version of deep copy, handling all the 3 situations:

  1. Table circular reference
  2. Keys which are also tables
  3. Metatable

The general version:

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

Or the table version:

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

Based on the lua-users.org/wiki/CopyTable's and Alan Yates' functions.

Oo萌小芽oO 2024-07-22 21:51:45

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

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

也许元表副本也应该是可选的?

An optionally deep, graph-general, recursive version:

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

Perhaps metatable copy should be optional also?

野生奥特曼 2024-07-22 21:51:45

这是我实际做的:

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

正如 Doub 提到的,如果你的表键不是严格单调递增的,它应该是 而不是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

它处理表和元表通过递归调用自身(这是它自己的奖励)。 聪明的一点是,您可以向其传递任何值(无论是否为表),并且它将被正确复制。 然而,代价是它可能会溢出堆栈。 因此,可能需要更强大(非递归)函数

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

Here's what I actually did:

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

As Doub mentions, if your table keys are not strictly monotonically increasing, it should be pairs not ipairs.

I also found a deepcopy function that is more robust:

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

It handles tables and metatables by calling itself recursively (which is its own reward). One of the clever bits is that you can pass it any value (whether a table or not) and it will be copied correctly. However, the cost is that it could potentially overflow the stack. So and even more robust (non-recursive) function might be needed.

But that's overkill for the very simple case of wanting to copy an array into another variable.

︶ ̄淡然 2024-07-22 21:51:45

(不幸的是,记录很少) stdlib 项目对随标准 Lua 发行版。 其中有一些关于表复制和合并主题的变体。

这个库也包含在 Lua for Windows 发行版中,并且应该成为任何认真的 Lua 用户的一部分工具箱。

手动实现此类操作时要确保的一件事是正确处理元表。 对于简单的表结构应用程序,您可能没有任何元表,并且使用 pairs() 的简单循环是可接受的答案。 但如果表用作树,或包含循环引用,或具有元表,那么事情会变得更加复杂。

The (unfortunately lightly documented) stdlib project has a number of valuable extensions to several of the libraries shipped with the standard Lua distribution. Among them are several variations on the theme of table copying and merging.

This library is also included in the Lua for Windows distribution, and should probably be a part of any serious Lua user's toolbox.

One thing to make sure of when implementing things like this by hand is the proper handling of metatables. For simple table-as-structure applications you probably don't have any metatables, and a simple loop using pairs() is an acceptable answer. But if the table is used as a tree, or contains circular references, or has metatables, then things get more complex.

感性 2024-07-22 21:51:45

不要忘记函数也是引用,因此如果您想完全“复制”所有值,您也需要获取单独的函数; 然而,我知道复制函数的唯一方法是使用 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))
                -- Problems may occur if the function has upvalues.
            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

Don't forget that functions are also references, so if you wanted to completely 'copy' all of the values you'd need to get separate functions, too; however, the only way I know to copy a function is to use loadstring(string.dump(func)), which according to the Lua reference manual, doesn't work for functions with upvalues.

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))
                -- Problems may occur if the function has upvalues.
            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
深巷少女 2024-07-22 21:51:45

我认为Lua的标准库中没有'table.copy()'的原因是因为任务定义不精确。 如此处所示,可以制作“一层深”的副本(您所做的),即考虑或不考虑可能的重复引用的深层副本。 然后是元表。

就我个人而言,我仍然希望他们提供内置功能。 只有当人们对其语义不满意时,他们才需要自己去做。 然而,实际上并不经常有按值复制的需求。

I think the reason why Lua doesn't have 'table.copy()' in its standard libraries is because the task is not precise to define. As shown already here, one can either make a copy "one level deep" (which you did), a deepcopy with or without caring of possible duplicate references. And then there's metatables.

Personally, I would still like them to offer a built-in function. Only if people wouldn't be pleased with its semantics, they would need to go do it themselves. Not very often, though, one actually has the copy-by-value need.

东走西顾 2024-07-22 21:51:45

警告:标记的解决方案不正确

当表包含表时,仍将使用对这些表的引用。 我花了两个小时寻找我所犯的错误,而这是因为使用了上面的代码。

所以你需要检查该值是否是一个表。 如果是,你应该递归调用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

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

Warning: the marked solution is INCORRECT!

When the table contains tables, references to those tables will still be used instead. I have been searching two hours for a mistake that I was making, while it was because of using the above code.

So you need to check if the value is a table or not. If it is, you should call table.copy recursively!

This is the correct table.copy function:

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

Note: This might also be incomplete when the table contains functions or other special types, but that is possible something most of us don't need. The above code is easily adaptable for those who need it.

何止钟意 2024-07-22 21:51:45

这与基本表格一样好。 如果您需要复制带有元表的表,请使用深度复制之类的东西。

That's as good as you'll get for basic tables. Use something like deepcopy if you need to copy tables with metatables.

最美不过初阳 2024-07-22 21:51:45

在大多数情况下,当我需要复制表时,我希望拥有一个不与原始表共享任何内容的副本,这样对原始表的任何修改都不会影响副本(反之亦然)。

到目前为止显示的所有片段都无法为可能具有共享密钥或与表共享密钥的表创建副本,因为这些密钥将继续指向原始表。 如果您尝试复制创建的表,很容易看出:a = {}; a[a] = a。 Jon 引用的 deepcopy 函数可以解决这个问题,所以如果您需要创建真实/完整的副本,应该使用deepcopy

In most of the cases when I needed to copy a table, I wanted to have a copy that doesn't share anything with the original, such that any modification of the original table has no impact on the copy (and vice versa).

All the snippets that have been shown so far fail at creating a copy for a table that may have shared keys or keys with tables as those are going to be left pointing to the original table. It's easy to see if you try to copy a table created as: a = {}; a[a] = a. deepcopy function referenced by Jon takes care of that, so if you need to create a real/full copy, deepcopy should be used.

箹锭⒈辈孓 2024-07-22 21:51:45

在这里使用笔灯库:
https://stevedonovan.github.io/Penlight/api/libraries /pl.tablex.html#deepcopy

local pl = require 'pl.import_into'()
local newTable = pl.tablex.deepcopy(oldTable)

Use penlight library here:
https://stevedonovan.github.io/Penlight/api/libraries/pl.tablex.html#deepcopy

local pl = require 'pl.import_into'()
local newTable = pl.tablex.deepcopy(oldTable)
假情假意假温柔 2024-07-22 21:51:45

只需使用

local unpack = unpack or table.unpack

list2 = {unpack (list)}

Just use the

local unpack = unpack or table.unpack

list2 = {unpack (list)}
忘年祭陌 2024-07-22 21:51:45

这可能是最简单的方法:

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

function table.copy(mytable)  --mytable = the table you need to copy

    newtable = {}

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

new_table = table.copy(data)  --copys the table "data"

This might be the simplest method:

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

function table.copy(mytable)  --mytable = the table you need to copy

    newtable = {}

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

new_table = table.copy(data)  --copys the table "data"
笙痞 2024-07-22 21:51:45

在我的情况下,当表中的信息只是数据和其他表(不包括函数,...)时,以下代码行是获胜的解决方案:

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

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

In my situation, when the information in the table is only data and other tables (excluding functions, ...), is the following line of code the winning solution:

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

I'm writing Lua code for some home automation on a Fibaro Home Center 2. The implementation of Lua is very limited with no central library of functions you can refer to. Every function needs to be declared in the code so to keep the code serviceable, so one line solutions like this are favorable.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文