首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Lua :高效地复制表格(深拷贝)

Lua :高效地复制表格(深拷贝)
EN

Stack Overflow用户
提问于 2017-02-11 17:14:06
回答 2查看 4K关注 0票数 3

我试着高效地制作一张lua桌子的副本。我编写了以下函数copyTable(),运行良好(参见下面)。但是我想我可以用函数的“传递价值”机制来实现更有效的功能。我做了一些测试来探索这个机制:

代码语言:javascript
复制
function nop(x)
  return x
end

function noop(x)
  x={}
  return x
end

function nooop(x)
  x[#x+1]=4
  return x
end

function copyTable(datatable)
  local tblRes={}
  if type(datatable)=="table" then
    for k,v in pairs(datatable) do tblRes[k]=copyTable(v) end
  else
    tblRes=datatable
  end
  return tblRes
end

tab={1,2,3}
print(tab)            -->table: 0x1d387e0 tab={1,2,3}
print(nop(tab))       -->table: 0x1d387e0 tab={1,2,3}
print(noop(tab))      -->table: 0x1e76f90 tab={1,2,3}
print(nooop(tab))     -->table: 0x1d387e0 tab={1,2,3,4}
print(tab)            -->table: 0x1d387e0 tab={1,2,3,4}
print(copyTable(tab)) -->table: 0x1d388d0

我们可以看到,对表的引用是通过函数(当我只是读取或添加内容时)传递的,除非在noop()中,我尝试对现有的表进行根本的修改。

我读过Bas BossinkMichael Andersonthis Q/A中的答案。将传递或表视为参数,他们强调了“ref传递的参数”和“由值传递的参数和表是引用的参数”之间的区别,并举例说明了这种差异。

但这究竟意味着什么呢?我们是否有引用的副本,但这与传递引用有什么不同,因为指向和操作的数据仍然是相同的,而不是复制的?当我们试图影响表的零时,noop()中的机制是特定的,是为了避免删除表,还是在哪种情况下它触发(我们可以用nooop()看到在修改表时情况并不总是这样)?

我的问题是:传递表的机制到底是如何工作的?有没有一种方法可以更有效地复制表中的数据,而无需承担copyTable的负担?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-02-11 17:36:15

在Lua中传递参数的规则类似于C:everything是通过值传递的,但是表和用户数据作为指针传递。传递引用的副本在用法上并没有太大的不同,但它与通过引用传递完全不同。

例如,你特别提到了这个部分。

代码语言:javascript
复制
function noop(x)
  x={}
  return x
end
print(noop(tab))      -->table: 0x1e76f90 tab={1, 2, 3}

您正在将新table1的值分配给变量x (x现在持有一个新指针值)。您没有修改原始表,tab变量仍然保存指向原始表的指针值。当您从noop返回时,您将返回新表的值,该值为空。变量保存值,指针是值,而不是引用。

编辑:

错过了你的另一个问题。不,如果你想要深拷贝一个表,一个类似于你所写的函数是唯一的方法。当桌子变大时,深拷贝是非常慢的。为了避免性能问题,您可以使用一种类似于“倒带表”的机制,它跟踪对它们所做的更改,以便在以后的时间点(在带有回溯上下文的递归中非常有用)进行撤消。或者,如果你只需要让用户不要乱动桌子内部的东西,就写一个“可冻结的”特征。

1假设{}语法是一个构造新表并返回指向新表的指针的函数。

票数 2
EN

Stack Overflow用户

发布于 2017-08-24 16:46:06

如果您确信这3种假设(A)对"tab“有效(正在复制的表):

  1. 没有桌子钥匙 t1 = {} tab = {} tabt1 =值
  2. 没有重复的表值。 t1 = {} tab = {} tab.a = t1 tab.b = t1 -或-tab.a.b.x= t1
  3. 没有递归表: 选项卡= {} tab.a = tab -或-- tab.a.b.x= tab

然后,您提供的代码是最小的,几乎尽可能高效。

如果A1不保存(即您有表键),则必须将代码更改为:

代码语言:javascript
复制
function copyTable(datatable)
  local tblRes={}
  if type(datatable)=="table" then
    for k,v in pairs(datatable) do 
      tblRes[copyTable(k)] = copyTable(v) 
    end
  else
    tblRes=datatable
  end
  return tblRes
end

如果A2不保持(即您有重复的表值),那么您可以将代码更改为:

代码语言:javascript
复制
function copyTable(datatable, cache)
  cache = cache or {}
  local tblRes={}
  if type(datatable)=="table" then
    if cache[datatable] then return cache[datatable]
    for k,v in pairs(datatable) do 
      tblRes[copyTable(k, cache)] = copyTable(v, cache) 
    end
    cache[datatable] = tblRes
  else
    tblRes=datatable
  end
  return tblRes
end

不过,只有当您有大量重复的大型表时,这种方法才会有回报。因此,这是一个评估哪个版本在实际生产场景中更快的问题。

如果A3不保持(即您有递归表),那么您的代码(以及上面的两个调整)将进入一个无限递归循环,并最终抛出堆栈溢出。

处理这一问题的最简单方法是,如果发生表递归,则保持回溯并抛出错误:

代码语言:javascript
复制
function copyTable(datatable, cache, parents)
  cache = cache or {}
  parents = parents or {}
  local tblRes={}
  if type(datatable)=="table" then
    if cache[datatable] then return cache[datatable]
    assert(not parents[datatable])
    parents[datatable] = true
    for k,v in pairs(datatable) do 
      tblRes[copyTable(k, cache, parents)]
        = copyTable(v, cache, parents) 
    end
    parents[datatable] = false
    cache[datatable] = tblRes
  else
    tblRes=datatable
  end
  return tblRes
end

我对深度复制函数的解决方案,它处理递归表,保留原始结构,可以在这里找到:https://gist.github.com/cpeosphoros/0aa286c6b39c1e452d9aa15d7537ac95

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/42178768

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档