如何按值复制 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
这就是你在基本表格中能得到的最好的了。如果你需要复制带有元表的表格,可以使用类似 deepcopy 的东西。
表格复制有许多潜在的定义。这取决于您是否需要简单或深度复制,是否需要复制、共享或忽略元表等。没有单个实现可以满足所有人。
一种方法是简单地创建一个新表格并复制所有键/值对:
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
仅迭代表格键的子集(即从一开始的连续正整数键按递增顺序)。
(不幸的是,文档相对较少的)stdlib项目拥有很多有价值的扩展,适用于标准Lua库的多个类别。其中有数个变体是关于表复制和合并的主题。
这个库也包含在Windows版Lua中,应该成为任何使用Lua时的基本工具箱的一部分。
当手动实现类似的功能时,确保正确处理元表是非常重要的。对于简单的表式结构应用程序来说,你可能没有任何元表,使用pairs()
简单循环是一个可接受的答案。但如果该表被用作树状结构,或包含循环引用,或拥有元表,则情况变得更为复杂。
我认为 Lua 没有在其标准库中提供“table.copy()”的原因是因为该任务不容易精确定义。正如此处已经展示的,人们可以进行“一级深度”的复制(你已经做到了),深度复制,是否考虑可能的重复引用,还有元表。
就个人而言,我仍然希望它们能提供一个内置函数。只有当人们对其语义感到不满意时,他们才需要自己去实现它。然而,实际上很少遇到需要进行按值复制的情况。
为了说明这一点,我个人的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
没有一种复制函数被广泛认可为"标准"。
一个可选的深度、图形通用、递归版本:
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的复制也应该是可选的呢?
在大多数需要复制表格的情况下,我希望得到一个与原始表格没有任何关联的副本,以便对原始表格的任何修改都不会对副本产生影响(反之亦然)。
迄今为止展示的所有代码片段都无法为可能具有共享键或与表相同的键的表格创建一个副本,因为这些键将指向原始表格。如果尝试复制创建为a={}; a[a] = a
的表格,则可以轻松地看到这一点。 Jon 引用的 deepcopy 函数将处理此问题,因此,如果需要创建一个真实/完整的副本,应使用 deepcopy
。
完整版本的深拷贝,处理以下三种情况:
- 表循环引用
- 键也是表的情况
- 元表
通用版本:
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 的函数。
不要忘记函数也是引用,因此,如果您想要完全“复制”所有值,则还需要获取单独的函数;然而,我所知道的唯一复制函数的方法是使用 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
警告:标记的解决方案是错误的!
当表格包含表格时,仍将使用对这些表格的引用。我已经搜索了两个小时的错误,而这是因为使用了上面的代码。
因此,您需要检查值是否为表格。如果是,您应该递归调用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
注意:当表格包含函数或其他特殊类型时,这也可能是不完整的,但这是大多数人不需要的可能性。上面的代码对于需要它的人来说很容易适应。
为了进行一些易读代码的高尔夫小游戏,这里提供了一个短版本来处理标准的棘手情况:
- 将表作为键
- 保留元表
- 递归表
我们可以用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
元方法的示例。
这可能是最简单的方法:
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"
在此处使用 Penlight 库:
https://stevedonovan.github.io/Penlight/api/libraries/pl.tablex.html#deepcopy
local pl = require 'pl.import_into'()
local newTable = pl.tablex.deepcopy(oldTable)
在我的情况下,当表格中的信息仅为数据和其他表格(不包括函数等)时,以下代码行是最优解:
local copyOfTable = json.decode( json.encode( sourceTable ) )
我正在为 Fibaro Home Center 2 上的一些家庭自动化编写 Lua 代码。Lua 的实现非常有限,没有可以引用的中央函数库。每个函数都需要在代码中声明,因此像这样的一行解决方案是可取的,以保持代码的可维护性。
只需使用
local unpack = unpack or table.unpack
list2 = {unpack(list)}
即可。
- 如何在roblox studio中1:1导入真实世界的地形?
- 求解,lua_resume的第二次调用继续执行协程问题。
- 【上海普陀区】内向猫网络招募【Skynet游戏框架Lua后端程序员】
- SF爱好求教:如何用lua实现游戏内调用数据库函数实现账号密码注册?
- Lua实现网站后台开发
- LUA错误显式返回,社区常见的规约是怎么样的
- lua5.3下载库失败
- 请问如何实现文本框内容和某个网页搜索框内容连接,并把网页输出来的结果反馈到另外一个文本框上
- lua lanes多线程使用
- 一个kv数据库
- openresty 有没有比较轻量的 docker 镜像
- 想问一下,有大佬用过luacurl吗
- 在Lua执行过程中使用Load函数出现问题
- 为什么 neovim 里没有显示一些特殊字符?
- Lua比较两个表的值(不考虑键的顺序)
- 有个lua简单的项目,外包,有意者加微信 liuheng600456详谈,最好在成都
- 如何在 Visual Studio 2022 中运行 Lua 代码?
- addEventListener 返回 nil Lua
- Lua中获取用户配置主目录的跨平台方法
- 如何编写 Lua 模式将字符串(嵌套数组)转换为真正的数组?
这是我实际上做的:
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
它通过递归调用本身来处理表和元表(这是它自己的报酬)。其中一个聪明的地方是你可以传递任何值(无论是表还是其他值),它都能正确地进行复制。然而,代价是它可能会潜在地溢出堆栈。因此,可能需要更健壮的(非递归)函数。
但是对于仅仅想要将一个数组复制到另一个变量的非常简单的情况来说,这是过度设计了。