如何按值复制 Lua 表?
最近我写了一些 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(16)
表副本有许多潜在的定义。 这取决于您想要简单复制还是深层复制,是否想要复制、共享或忽略元表等。没有单一的实现可以满足所有人的要求。
一种方法是简单地创建一个新表并复制所有键/值对:
请注意,您应该使用
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:
Note that you should use
pairs
instead ofipairs
, sinceipairs
only iterate over a subset of the table keys (ie. consecutive positive integer keys starting at one in increasing order).只是为了说明这一点,我个人的
table.copy
也关注元表:没有足够广泛的复制函数被称为“标准”。
Just to illustrate the point, my personal
table.copy
also pays attention to metatables:There is no copy function sufficiently widely agreed upon to be called "standard".
为了玩一点可读代码高尔夫,这里有一个简短的版本,它处理标准的棘手情况:
我们可以用 7 行来完成此操作:
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:
We can do this in 7 lines:
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.深度复制的完整版本,处理所有 3 种情况:
通用版本:
或表版本:
基于 lua-users.org/wiki/CopyTable 和 Alan Yates' 功能。
The full version of deep copy, handling all the 3 situations:
The general version:
Or the table version:
Based on the lua-users.org/wiki/CopyTable's and Alan Yates' functions.
可选的深度、通用图形、递归版本:
也许元表副本也应该是可选的?
An optionally deep, graph-general, recursive version:
Perhaps metatable copy should be optional also?
这是我实际做的:
正如 Doub 提到的,如果你的表键不是严格单调递增的,它应该是
对
而不是ipairs
。我还发现了一个更强大的
deepcopy
函数:它处理表和元表通过递归调用自身(这是它自己的奖励)。 聪明的一点是,您可以向其传递任何值(无论是否为表),并且它将被正确复制。 然而,代价是它可能会溢出堆栈。 因此,可能需要更强大(非递归)函数。
但对于想要将数组复制到另一个变量的非常简单的情况来说,这太过分了。
Here's what I actually did:
As Doub mentions, if your table keys are not strictly monotonically increasing, it should be
pairs
notipairs
.I also found a
deepcopy
function that is more robust: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.
(不幸的是,记录很少) 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.不要忘记函数也是引用,因此如果您想完全“复制”所有值,您也需要获取单独的函数; 然而,我知道复制函数的唯一方法是使用 loadstring(string.dump(func)),根据 Lua 参考手册,它不适用于具有上值的函数。
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.我认为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.
警告:标记的解决方案不正确!
当表包含表时,仍将使用对这些表的引用。 我花了两个小时寻找我所犯的错误,而这是因为使用了上面的代码。
所以你需要检查该值是否是一个表。 如果是,你应该递归调用table.copy!
这是正确的 table.copy 函数:
注意:当表包含函数或其他特殊类型时,这也可能不完整,但这可能是我们大多数人不需要的。 上面的代码很容易适应那些需要它的人。
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:
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.
这与基本表格一样好。 如果您需要复制带有元表的表,请使用深度复制之类的东西。
That's as good as you'll get for basic tables. Use something like deepcopy if you need to copy tables with metatables.
在大多数情况下,当我需要复制表时,我希望拥有一个不与原始表共享任何内容的副本,这样对原始表的任何修改都不会影响副本(反之亦然)。
到目前为止显示的所有片段都无法为可能具有共享密钥或与表共享密钥的表创建副本,因为这些密钥将继续指向原始表。 如果您尝试复制创建的表,很容易看出:
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.在这里使用笔灯库:
https://stevedonovan.github.io/Penlight/api/libraries /pl.tablex.html#deepcopy
Use penlight library here:
https://stevedonovan.github.io/Penlight/api/libraries/pl.tablex.html#deepcopy
只需使用
Just use the
这可能是最简单的方法:
This might be the simplest method:
在我的情况下,当表中的信息只是数据和其他表(不包括函数,...)时,以下代码行是获胜的解决方案:
我正在为 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:
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.