为什么将一个表作为值分配给另一个表会导致问题?

Gal*_*har 1 lua

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

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

b = {}
b = a.b

Run Code Online (Sandbox Code Playgroud)

我在执行此操作时遇到了一些奇怪的错误。如果我使用如下所示的表克隆函数,它会正常工作,我只是不明白为什么首先需要使用克隆函数/最佳实践。

很难描述我在尝试执行第一种方法时遇到的错误,但基本上,如果我尝试在 的a.b部分中添加其他键值b = a.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
Run Code Online (Sandbox Code Playgroud)

然后执行以下操作可以消除任何错误

b = deepCopy(a.b)
Run Code Online (Sandbox Code Playgroud)

Nic*_*las 5

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

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

考虑以下:

tbl1 = { 1, 2, 3 }
tbl2 = tbl1
tbl3 = { 1, 2, 3 }
Run Code Online (Sandbox Code Playgroud)

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

tbl3不是同一个tbl1。它们可能具有逻辑上相同的内容,但对于Lua而言,它们是不同的表。操作存储在 中的表的内容tbl3不会影响任何查看存储在tbl1或中的表的人tbl2

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

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

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

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

深度复制也没有标准的函数或机制。原因很简单:进行深度复制的方法有很多种,从简单到复杂。例如,您的简单deepCopy函数会破坏(递归地)存储自身的表:

me = { a = 4, other = {} }
me.other.me = me
Run Code Online (Sandbox Code Playgroud)

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

如果 Lua 的标准库有一个深度复制函数,那么它要么会处理所有这样的情况(因此成本高昂),要么会是一个更简单的函数,可能会在任意数量的极端情况下崩溃(在桌子等)。

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