函数的功能是浅拷贝一个值。它只会拷贝原值的外层结构,然后把原结构中的各个内部对象原封不动地塞到这个新的结构中。也就是说,新的结构加上原有的内部对象就形成了新的值。

    这些原有的内部对象可能是原语类型的值,也可能是复合类型的值,另外还可能是更加复杂的值,比如容器。就拿数组来说,它的内部对象通常指的是其中的元素值。至于它们有多复杂,就要看数组的元素类型是什么了。

    对于元素类型属于原语类型的数组来说,从表面上看,浅拷贝与深拷贝并没有什么区别。例如:

    我利用copy函数得到了数组array_orig1的复本,并将其赋给了array_copy1变量。然后,我又使用索引表达式为这个复本中的第一个元素赋予了新的值。可以看到,即使我没有采用深拷贝,这种改变也不会影响到原数组array_orig1

    再来看元素类型属于复合类型的数组。我们对其复本中的元素值进行替换仍然不会影响到原数组。不过,有一些改变却会给这类的原数组带来实质性的影响。请看下面的代码:

    1. julia> mutable struct Person
    2. name::String
    3. age::UInt8
    4. extra
    5. end
    6. julia> p1 = Person("Robert", 37, "Beijing"); p2 = Person("Andres", 32, "Madrid"); p3 = Person("Eric", 28, "Paris");
    7. julia> array_orig2 = Person[p1, p2, p3]
    8. 3-element Array{Person,1}:
    9. Person("Robert", 0x25, "Beijing")
    10. Person("Andres", 0x20, "Madrid")
    11. Person("Eric", 0x1c, "Paris")
    12. julia> array_copy2 = copy(array_orig2)
    13. 3-element Array{Person,1}:
    14. Person("Robert", 0x25, "Beijing")
    15. Person("Andres", 0x20, "Madrid")
    16. Person("Eric", 0x1c, "Paris")
    17. julia>

    数组array_orig2的元素类型是Person,是一个可变的复合类型。该数组包含了 3 个元素值,分别是我刚刚定义的p1p2p3。数组array_copy2array_orig2的复本,同样是由copy函数创建的。

    1. Person("Mark", 0x2d, "New York")
    2. julia> show(array_orig2)
    3. Person[Person("Robert", 0x25, "Beijing"), Person("Andres", 0x20, "Madrid"), Person("Eric", 0x1c, "Paris")]
    4. julia>

    实际上,无论数组的元素类型是什么,通过索引表达式替换元素值的操作都不会对原数组产生任何的影响。但是,如果我们改变的是某个元素值的内部对象,那么结果就截然不同了。就像下面这样:

    我修改了array_copy2中的第一个元素值,把它的字段age的值改成了38。可以看到,这一操作使得array_orig2中对应元素值里的age字段的值也发生了同样的改变。

    其实,array_copy2中的第一个元素值就是array_orig2中的第一个元素值本身,也就是说它们完全是同一个值。所以,我对前者的修改实际上就是在修改后者。而且,对于其中任何在对应位置上的元素值来说都会是如此。其根本原因就是,数组array_copy2是经由对数组array_orig2的浅拷贝而得出的复本。copy函数只拷贝了原数组的外层结构给新数组,却在新数组上直接沿用了原数组中的所有元素值。

    不但如此,我们对原Person类型值的修改,也会立即反应到这两个数组中相应的元素值上。例如:

    1. julia> p1.age = 25
    2. 25
    3. julia> Int(array_orig2[1].age), Int(array_copy2[1].age)
    4. (25, 25)
    5. julia>

    这再次印证了 Julia 中的值总是以共享的方式被传递的(passed by sharing)。数组array_orig2array_copy2中的第一个元素值实际上都是p1的值本身。p2p3的情况也是这样。

    如果我们在元素类型属于容器类型的数组上做浅拷贝,显然也会发生类似的事情。比如:

    1. julia> a1 = [1, 3, 5]; a2 = [2, 4, 6];
    2. julia> array_orig3 = [a1, a2]; array_copy3 = copy(array_orig3);
    3. (30, 30)

    深拷贝除了会拷贝原值的外层结构,还会把原结构中的所有内部对象都复制一遍,无论这些内部对象藏得有多么深。也就是说,新的结构和新的内部对象共同形成了全新的值。如此一来,复本与原值就可以做到相互独立、毫不相干了。示例如下:

    函数deepcopy用于对一个值进行深拷贝。我利用它得到了数组array_orig3的复本array_deepcopy3。我虽然可以通过a1[2] = 60修改数组array_orig3中的元素值,但无法通过同样的方式对数组array_deepcopy3进行修改。注意,在前一个例子执行完之后,array_orig3的值已变为[[1, 30, 5], [2, 4, 6]],而array_deepcopy3正是基于那时的它的复本。因此,索引表达式array_deepcopy3[1][2]的结果值才会是30

    我们来看一个更复杂一些的例子:

    1. julia> a3 = [7, 9, 11]; a4 = [8, 10, 12];
    2. julia> array_orig4 = [[a1, a3], [a2, a4]]
    3. 2-element Array{Array{Array{Int64,1},1},1}:
    4. [[1, 60, 5], [7, 9, 11]]
    5. [[2, 4, 6], [8, 10, 12]]
    6. julia> array_deepcopy4 = deepcopy(array_orig4);
    7. julia>

    数组array_orig4中的每一个元素值都分别是一个数组,并且在这些数组之中还有数组。也就是说,array_orig4是数组的数组的数组,即一个拥有三层结构的数组。而数组array_deepcopy4则产生自对array_orig4的深度拷贝。下面我们来看看deepcopy函数是否已将其中的所有内部对象全都拷贝了出来。代码如下:

    1. julia> a4[1] = 80; array_orig4[2][2][1], array_deepcopy4[2][2][1]
    2. (80, 8)
    3. julia> array_orig4[2][2][1] = 82; array_deepcopy4[2][2][1]
    4. 8

    显然,无论内部对象有多么复杂,由deepcopy函数产生的数组复本都是完全独立于原数组的。

    好了,现在我们知道了,拷贝像数组这样的对象很容易,调用一个函数就可以了。copy函数用于浅拷贝,它只会拷贝原数组的外层结构,并沿用其所有的元素值。deepcopy函数用于深拷贝,它不仅会拷贝原数组的外层结构,而且还会拷贝其所有的元素值以及更深层次的内部对象(如果有的话)。