18.深拷贝

  1. 18.深拷贝
    1. 18.1 知识点
      1. 什么是深拷贝?
      2. 如何进行深拷贝?
    2. 18.2 知识点代码

18.深拷贝


18.1 知识点

什么是深拷贝?

在Lua中,使用赋值运算符”=”进行拷贝的时候,分两种情况:

  1. string、number、boolean这些基本类型,会进行复制,会创建一个新对象,拷贝出来的对象和原来的互不影响

    local num1 = 5
    local num2 = num1
    num2 = 10
    print(num1) -- 5
    print(num2) -- 10
    
    local str1 = "test123"
    local str2 = str1
    str2 = "韬老狮"
    print(str1) -- test123
    print(str2) -- 韬老狮
    
  2. table类型,是直接进行的引用,拷贝出来的对象和原来是同一个对象,改一处另一处也会变化

    local tbl = { x = 1, y = 2, z = 3 }
    local tb2 = tbl
    tb2.x = 4
    print(tbl.x) -- 4
    print(tb2.x) -- 4
    

因此,一般我们提到Lua中的深拷贝,一般都是希望对table类型的变量实现深拷贝。即拷贝后的内容变化,不会影响原来的内容。而Lua中并没有提供这样的api,因此我们一般会自己封装一个函数。

如何进行深拷贝?

进行table深拷贝整体的封装思路就是递归地遍历表的每一个元素,并且在遇到子表时,对子表也进行深拷贝。这样可以确保拷贝后的新表与原表完全独立,任何对新表的修改都不会影响到原表。

function clone(object)
    -- 记录已经拷贝过的表,防止循环引用
    -- 有可能表自己引用自己,如果不记录有可能无限递归
    local lookup_table = {}

    -- 递归拷贝函数
    local function _copy(object)
        -- 如果遇到的不是表则直接返回这个值(比如nil或者基本类型)
        if type(object) ~= "table" then
            return object

        -- 如果在拷贝过的表中找到该对象,说明已经拷贝过,直接返回拷贝过的表
        elseif lookup_table[object] then
            return lookup_table[object]
        end

        -- 创建新表
        local new_table = {}

        -- 记录这个新表 表明object这个表已经拷贝过
        lookup_table[object] = new_table

        -- 递归拷贝每个键值对 对每个键值对都调用_copy拷贝
        -- 之所以对键也调用_copy函数,是因为键也可能是一个表
        for key, value in pairs(object) do
            new_table[_copy(key)] = _copy(value)
        end

        -- 为拷贝表设置与原来的表相同的元表
        return setmetatable(new_table, getmetatable(object))
    end

    return _copy(object)
end

local tb3 = { x = 1, y = 2, z = 3 }
local tb4 = clone(tb3)
tb4.x = 4
print(tb3.x) -- 1
print(tb4.x) -- 4

其中不太好理解的代码为:

-- 递归拷贝每个键值对 对每个键值对都调用_copy拷贝
-- 之所以对键也调用_copy函数,是因为键也可能是一个表
for key, value in pairs(object) do
    new_table[_copy(key)] = _copy(value)
end

我们举个例子来理解这里的代码:

local original = {
    a = { 1, 2, 3 },
    b = "string",
    c = 42,
    d = { x = 1, y = 2 }
}

local copy = clone(original)

对于键a,值{1,2,3},调用_copy(a)返回a本身,调用_copy({1,2,3})会递归地创建一个新表{1,2,3}

对于键b,值”string”,调用_copy(b)返回b本身,调用_copy(“string”)会返回”string”

对于键c,值42,调用_copy(c)返回c本身,调用_copy(42)会返回42

对于键d,值{x=1,y=2},调用_copy(d)返回d本身,调用_copy({x=1,y=2})会递归地创建一个新表{x=1,y=2}

每次递归调用_copy函数时,都会对原表中的键和值进行深拷贝,并将结果插入到新表”new_table”中。这样就确保了新表和原表之间完全独立。


18.2 知识点代码

print("**********深拷贝************")

print("**********知识点--  什么是深拷贝?************")

-- 在Lua中,使用赋值运算符"="进行拷贝的时候,分两种情况:

-- 1.string、number、boolean这些基本类型,会进行复制,会创建一个新对象,拷贝出来的对象和原来的互不影响
local numl = 5
local num2 = numl
num2 = 10
print(numl) -- 5
print(num2) -- 10
local strl = "testl23"
local str2 = strl
str2 = "韬老狮"
print(strl) -- testl23
print(str2) -- 韬老狮

-- 2.table类型,是直接进行的引用,拷贝出来的对象和原来是-一个对象,改一处另一处也会变化
local tbl = { x = 1, y = 2, z = 3 }
local tb2 = tbl
tb2.x = 4
print(tbl.x) -- 4
print(tb2.x) -- 4

-- 因此,一般我们提到Lua中的深拷贝,一般都是希望对table类型的变量实现深拷贝。即拷贝后的内容变化,不会影响原来的内容。
-- 而Lua中并没有提供这样的api,因此我们一般会自己封装一个函数。

print("**********知识点二 如何进行深拷贝?************")

-- 进行table深拷贝整体的封装思路就是递归地遍历表的每一个元素,并且在遇到子表时,对子表也进行深拷贝。这样可以确保拷贝后的新表与原表完全独立,任何对新表的修改都不会影响到原表。

function clone(object)
    -- 记录已经拷贝过的表,防止循环引用
    -- 有可能表自己引用自己,如果不记录有可能无限递归
    local lookup_table = {}

    -- 递归拷贝函薮
    local function _copy(object)
        -- 如果遇到的元是表则直接返回这个值(比如nil或者基本类型)
        if type(object) ~= "table" then
            return object

            -- 如果在拷贝过的表中找到该对象,说明已经拷贝过,直接返回拷贝过的表
        elseif lookup_table[object] then
            return lookup_table[object]
        end


        -- 创建新表
        local new_table = {}

        -- 记录这个新表 表明objcet这个表已经拷贝过来
        lookup_table[object] = new_table

        -- 递归拷贝每个键值对 对每个键值对都调用_copy拷贝
        -- 之所以对键也调用_copy函数,是因为键也看可能是一个表
        for key, value in pairs(object) do
            new_table[_copy(key)] = _copy(value)
        end

        -- 为拷贝表设置与原来的表相同的元表
        return setmetatable(new_table, getmetatable(object))
    end

    return _copy(object)
end

local tb3 = { x = 1, y = 2, z = 3 }
local tb4 = clone(tb3)
tb4.x = 4
print(tb3.x) -- 1
print(tb4.x) -- 4

--其中不太好理解的代码为

-- 递归拷贝每个键值对 对每个键值对都调用_copy拷贝
-- 之所以对键也调用_copy函数,是因为键也看可能是一个表
-- for key, value in pairs(object) do
--     new_table[_copy(key)] = _copy(value)
-- end

-- 我们举个例子来理解这里的代码

local original =
{
    a = { 1, 2, 3 },
    b = "string",
    c = 42,
    d = { x = 1, y = 2 }
}

local copy = clone(original)

-- 对于键a,值{1,2,3},调用_copy(a)返回a本身,调用_copy({1,2,3})会递归的创建一个新表{1,2,3}
-- 对于键b,值"string",调用_copy(b)返回b本身,调用_copy("string")会返回"string"
-- 对于键c,值"42,调用_copy(c)返回c本身,调用_copy(42)会返回42
-- 对于键d,值{x=1,y=2},调用_copy(d)返回d本身,调用_copy({x=1,y=2})会递归的创建一个新表{x=1,y=2}
-- 每次递归调用_copy函数时,都会对原表中的键和值进行深拷贝,并将结果插入到新表"new_table"中。这样就确保了新表和原表之间完全独立


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏