【问题标题】:Why does assigning a table as a value to another table cause problems?为什么将一个表作为值分配给另一个表会导致问题?
【发布时间】:2019-08-30 23:48:15
【问题描述】:

为什么我们不能像这样在 Lua 中直观地复制表格:

a = {
  a = {},
  b = {},
}

b = {}
b = a.b

我在这样做时遇到了一些奇怪的错误。如果我使用如下表克隆函数,它会正常工作,我只是不明白为什么必须首先使用克隆函数/最佳实践。

很难描述我在尝试执行第一种方法时遇到的错误,但基本上,如果我尝试在b = a.ba.b 部分中添加额外的键值,那么额外的键-价值观并不总是我设定的那样。

function deepCopy(object)
    local lookup_table = {}
    local function _copy(object)
        if type(object) ~= "table" then
            return object
        elseif lookup_table[object] then
            return lookup_table[object]
        end
        local new_table = {}
        lookup_table[object] = new_table
        for index, value in pairs(object) do
            new_table[_copy(index)] = _copy(value)
        end
        return setmetatable(new_table, getmetatable(object))
    end
    return _copy(object)
end

然后执行以下操作会删除所有错误

b = deepCopy(a.b)

【问题讨论】:

  • 你能举一个错误的例子吗?可能尝试使用您的 b = a.b 示例复制该行为
  • 存储在变量中的表(作为 Lua 值)只是对实际表的引用。复制变量只会创建对同一个表的另一个引用。要构建表的副本,您必须手动完成。

标签: lua


【解决方案1】:

在 Lua 中,一个表就是一个值,每个不同的表都有一个不同的值。表的值用于标识其内容,但表的内容在概念上不是表的值。也就是说,要访问表的内容,需要表的值,但表的值与其内容不同。

表格的值可以存储在任何变量中。同样,该值用于标识该表并访问该表的内容,但这与逻辑上作为表内容的值不同。

考虑以下几点:

tbl1 = { 1, 2, 3 }
tbl2 = tbl1
tbl3 = { 1, 2, 3 }

tbl1tbl2的值是一样的;这意味着它们都引用同一个表,因此您可以通过任一变量访问该表的内容。所以 tbl1[2]tbl2[2] 不要简单地返回 2;他们都访问同一张表

tbl3tbl1不是同一张表。它们可能具有逻辑上相同的内容,但就 Lua 而言,它们是不同的表。操作存储在tbl3 中的表的内容不会影响查看存储在tbl1tbl2 中的表的任何人。

那么,为什么将表存储到变量中不会复制表的内容?几个原因。

  1. 深拷贝昂贵。如果所有副本都很深,您甚至无法在不执行副本的情况下执行简单的return {1, 2, 3}。毫无意义的副本,因为没有其他变量可以与该表对话(因为它是在原位创建的)。为什么要浪费性能?将表作为参数传递给函数或任何其他东西也是如此。

  2. 仅深度复制可防止有用的事情,例如从不同位置访问同一个表。如果每个表副本都很深,你怎么能拥有像模块表的本地副本这样简单的东西呢?您不能让表“成员函数”返回对象内部的表,因此您可以使用该表来操作该对象中的数据,因为该返回必须复制表。因此,表对象只能通过直接成员函数来改变。

深度复制是一个有用的工具。但这不是默认值,因为它不应该是。大多数复制表的情况不需要它,用户需要一种从多个位置访问表的方法。

深度复制也没有标准的功能或机制。原因很简单:深度复制有很多方法,从简单到复杂。例如,您的简单 deepCopy 函数会在存储(递归)自身的表上中断:

me = { a = 4, other = {} }
me.other.me = me

这是 100% 有效的,你的 deepCopy 函数会在它上面中断。有一些方法可以实现deepCopy,这样它就可以处理这个问题,但它们既复杂又昂贵。大多数用户不需要可以处理递归对象的deepCopy

如果 Lua 的标准库有一个深拷贝函数,那么它要么可以处理所有这样的情况(因此很昂贵),要么它会是一个更简单的,可以在任何数量的极端情况下中断(对表中的同一张表等)。

因此,最好让深拷贝的任何潜在用户坐下来,准确地决定他们想要处理哪些情况,哪些不想处理。

【讨论】:

    【解决方案2】:
    • 变量保存引用,而不是整个表。
    • 复制引用比复制整个表要高效得多。
    • 函数调用有效地将参数分配给该函数的参数,因此如果分配是完整复制,则不可能编写修改表的函数。
    • 通常,当我们将表分配给某个对象时,我们要么 (a) 不打算修改表,要么 (b) 明确打算使用至少一个变量来修改基础表。请参阅上一点关于函数的内容。这意味着默认情况下进行完整复制会浪费资源。

    我的建议是只在你真正需要的时候复制表,除非你真的需要一个深拷贝,否则更喜欢浅拷贝。实际上,当我需要复制表时,我通常会编写一个专门的复制函数,所以我不会复制超过我需要的内容。

    【讨论】:

      猜你喜欢
      • 2021-12-10
      • 2019-07-01
      • 1970-01-01
      • 2016-06-25
      • 2021-02-17
      • 1970-01-01
      • 1970-01-01
      • 2012-06-24
      • 1970-01-01
      相关资源
      最近更新 更多