如何获取Julia复合类型的深度副本?

如何获取Julia复合类型的深度副本?,julia,Julia,这就是背景。我有多个用自己的字段和构造函数定义的复合类型。让我们在此处显示两个简化组件: type component1 x y end type component2 x y z end 现在我想定义一个新类型,这样它就可以在其中保存一个大小为K的数组,其中包含以前定义的复合类型。因此,它是一个具有两个字段的参数化复合类型:一个是整数K,另一个是传递类型的大小为K的数组 type mixture{T} components::Array{T,

这就是背景。我有多个用自己的字段和构造函数定义的复合类型。让我们在此处显示两个简化组件:

type component1
    x
    y
end

type component2
    x
    y
    z
end
现在我想定义一个新类型,这样它就可以在其中保存一个大小为K的数组,其中包含以前定义的复合类型。因此,它是一个具有两个字段的参数化复合类型:一个是整数K,另一个是传递类型的大小为K的数组

type mixture{T}
    components::Array{T, 1}
    K::Int64

    function mixture(qq::T, K::Int64)
        components = Array{typeof(qq), K}
        for k in 1:K
            components[k] = qq
        end
        new(components, K)
    end
end

但这不是正确的方法。因为所有的K分量都指向一个对象并操纵混合。分量[K]将影响所有的K分量。在python中,我可以用deepcopy解决这个问题。但是Julia中的deepcopy没有为复合类型定义。如何解决此问题?

对您的具体问题的回答:

在Julia中定义新类型时,通常会将
Base
中的一些标准方法扩展到新类型,包括
deepcopy
。例如:

type MyType
    x::Vector
    y::Vector
end
import Base.deepcopy
Base.deepcopy(m::MyType) = MyType(deepcopy(m.x), deepcopy(m.y))
现在,您可以通过
MyType
的实例调用
deepcopy
,您将得到一个新的真正独立的
MyType
副本作为输出

注意,我的
import Base.deepcopy
实际上是多余的,因为我在函数定义中引用了
Base
,例如
Base.deepcopy(m::MyType)
。然而,我做了这两件事是为了向您展示从
Base
扩展方法的两种方法

第二个注意事项是,如果您的类型有很多字段,您可以使用
deepcopy
对这些字段进行迭代,如下所示:

Base.deepcopy(m::MyType) = MyType([ deepcopy(getfield(m, k)) for k = 1:length(names(m)) ]...)
Base.length(m::Mixture) = length(m.components)
对代码的评论:

首先,Julia的标准做法是将类型名称大写,例如
Component1
而不是
Component1
。当然,你不必这么做,但是

第二,来自:为复合类型的字段声明特定类型。注意,您可以参数化这些声明,例如

type Component1{T1, T2}
    x::T1
    y::T2
end
第三,以下是我如何定义您的新类型:

type Mixture{T}
    components::Vector{T}
    Mixture{T}(c::Vector{T}) = new(c)
end
Mixture{T}(c::Vector{T}) = Mixture{eltype(c)}(c)
Mixture(x, K::Int) = Mixture([ deepcopy(x) for k = 1:K ])
我的代码和你的代码之间有几个重要的区别。我会一次看一个

您的
K
字段是多余的(我认为),因为它似乎只是
组件的长度。因此,将
length
方法扩展到新类型可能更简单,如下所示:

Base.deepcopy(m::MyType) = MyType([ deepcopy(getfield(m, k)) for k = 1:length(names(m)) ]...)
Base.length(m::Mixture) = length(m.components)
现在您可以使用
length(m)
,其中
m
mixed
的一个实例,以获取先前存储在
K
字段中的内容

类型
混合物中的内部构造函数不寻常。标准做法是,内部构造函数获取与类型字段一一对应(按顺序)的参数,然后内部构造函数的其余部分只执行初始化类型时希望执行的任何错误检查。您偏离了这一点,因为
qq
不一定是数组。这种行为最好保留给外部构造函数。那么,我对构造函数做了什么

mixed
的内部构造函数除了通过
new
将参数传递到字段之外,实际上不做任何事情。这是因为目前没有任何需要执行的错误检查(但我可以在将来轻松添加一些)

如果我想调用这个内部构造函数,我需要编写类似于
m=mixed{MyType}(x)
,其中
x
Vector{MyType}
。这有点烦人。因此,我的第一个外部构造函数使用
eltype(x)
自动推断
{…}
的内容。由于我的第一个外部构造函数,我现在可以使用
m=mixed(x)
而不是
m=mixed{MyType}(x)
初始化
mixed

我的第二个外部构造函数对应于您的内部构造函数。在我看来,您在这里追求的行为是在
组件的每个字段中使用相同的组件初始化
混合物
,重复
K次。因此,我通过对
x
的循环理解来实现这一点,只要为
x
定义了
deepcopy
方法,该方法就可以工作。如果不存在
deepcopy
方法,您将得到
no method exists
错误。这种编程称为duck类型,在Julia中,使用它通常不会造成性能损失

注意,我的第二个外部构造函数将调用我的第一个外部构造函数
K
次,每次,我的第一个外部构造函数将调用我的内部构造函数。在更复杂的场景中,这种嵌套功能将大大减少代码重复


对不起,我知道这有很多要接受的。希望能有所帮助。

谢谢您的完整回复。我熟悉多次分派的概念,但不确定是否最好的方法是修改Base.deepcopy。也谢谢你的评论。关于第三条注释,不需要在构造函数中声明类型,尽管我设法使用了您的方法,但我不太理解它@我已经更新了我的答案。如果仍然不清楚,请告诉我,我会再试一次:-)谢谢。现在更清楚了(对于我这个朱莉娅:D的初学者来说)