18.深拷贝
18.1 知识点
什么是深拷贝?
在Lua中,使用赋值运算符”=”进行拷贝的时候,分两种情况:
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) -- 韬老狮
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