Lua中的深拷贝与浅拷贝

有关深拷贝、浅拷贝在Lua中的一系列事情。

### 为什么要写深拷贝:

在《Lua进程设计(第四版)》第五章开头,作者描述了表的性质:

已经描述的很清楚了,对于表的操作,Lua语言不会进行深拷贝(即Lua语言拷贝的是对象的引用而非整个对象本身。)

先看一下Lua的源码:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
Table * (lua_State *L) {  
  GCObject *o = luaC_newobj(L, LUA_TTABLE, sizeof(Table));  
  Table *t = gco2t(o);  
  t->metatable = NULL;  
  t->flags = cast_byte(~0);  
  t->array = NULL;  
  t->sizearray = 0;  
  setnodevector(L, t, 0);  
  return t;  
}  

—|—

当生成一个Table时,会返回一个Table类型的指针(t),可以看出,如果在Lua中进行表的赋值的话,实际上是将一个表的指针赋给了另一个变量,而这两个量指向的是同一个地址,也就是说,这次赋值并没有创造出一个新的副本出来,而只是给原来的变量起了一个别名。

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
table1 = {1,2,3}  
table2 = table1  
table.insert(table1,4)  
  
for i,v in ipairs(table2) do  
    print(v)  
end  
  
>> 	1  
		2  
		3  
		4  

—|—

如果要复制出一份独立的表,该怎么写呢?

### 深拷贝与浅拷贝:

#### 深拷贝(DeepCopy):

拷贝整个对象本身,在上例的Lua代码中,如果table2对table1进行深拷贝,则改变table1不会同时改变table2,即table2是table1完全独立的一份复制。

#### 深拷贝的实现方式:

具体讲解可以参考wiki上的这一篇:CopyTable

##### 一种快速但不怎么好的实现:

这个版本的clone使用到了标准库中的unpack函数,这个函数会返回列表中的元素。

1  
2  
3  
4  
5  
6  
7  
8  
function  (org)  
   return { table.unpack(org)}  
end  
  
local abc = {5,12,1}  
local def = table.clone(abc)  
table.sort(def)  
print(abc [2],def [2])-  12 5  

—|—

[](https://bitzhangmo.github.io/#Shallow-Copy%EF%BC%9A “Shallow

Copy:”)Shallow Copy:

这个实现很简单,但是有一些缺陷:它只复制了顶层的值,没有对更加深层的元素、元表和特殊类型(如userdata或coroutines)进行处理,它也容易受到__pairs元方法的影响。

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
function shallowcopy(orig)  
    local orig_type = type(orig)  
    local copy  
    if orig_type == 'table' then  
        copy = {}  
        for orig_key, orig_value in pairs(orig) do  
            copy[orig_key] = orig_value  
        end  
    else   
        copy = orig  
    end  
    return copy  
end  

—|—

Deep

Copy:

这个版本的实现可以复制所有层级的元素,是一个简单的递归实现,并且会将原表的元表复制一份到新表的元表中。

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
function deepcopy(orig)  
    local orig_type = type(orig)  
    local copy  
    if orig_type == 'table' then  
        copy = {}  
        for orig_key, orig_value in next, orig, nil do  
            copy[deepcopy(orig_key)] = deepcopy(orig_value)  
        end  
        setmetatable(copy, deepcopy(getmetatable(orig)))  
    else   
        copy = orig  
    end  
    return copy  
end  

—|—

将表的拷贝存储在copies中,由原始的表索引,这是通过创建已复制的表的哈希表并将其作为第二个参数提供给deepcopy函数来完成的。

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
function deepcopy(orig, copies)  
    copies = copies or {}  
    local orig_type = type(orig)  
    local copy  
    if orig_type == 'table' then  
        if copies[orig] then  
            copy = copies[orig]  
        else  
            copy = {}  
            for orig_key, orig_value in next, orig, nil do  
                copy[deepcopy(orig_key, copies)] = deepcopy(orig_value, copies)  
            end  
            copies[orig] = copy  
            setmetatable(copy, deepcopy(getmetatable(orig), copies))  
        end  
    else   
        copy = orig  
    end  
    return copy  
end  

—|—

糖果

糖果
LUA教程

Lapis框架的常用处理方法

Lapis框架的常用处理方法 Continue reading

MoonScript实现选择排序

Published on February 26, 2017

MoonScript与Redis客户端

Published on January 19, 2017