2015-06-17 78 views
7

所以这里是设置。我有多个复合类型用他们自己的字段和构造函数定义。让显示此处为两个简化的组件:如何获取Julia复合类型的深层副本?

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分量都指向一个对象,并且操作mixture.components [k]将影响所有K分量。在python中,我可以用deepcopy来解决这个问题。但Julia的深层拷贝没有为复合类型定义。我该如何解决这个问题?

回答

11

的回答您的具体问题:

当你定义在朱莉娅一个新的类型,常见的是一些在Base标准方法扩展到新的类型,包括deepcopy。例如:

type MyType 
    x::Vector 
    y::Vector 
end 
import Base.deepcopy 
Base.deepcopy(m::MyType) = MyType(deepcopy(m.x), deepcopy(m.y)) 

现在,您可以拨打deepcopy超过MyType一个实例,你会得到的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)) ]...) 

你的代码中的注释:

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

二,从Julia docs performance tips:声明复合类型的字段的具体类型。请注意,您可以参数化这些声明,例如

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字段是多余的(我认为),因为它似乎只是components的长度。所以它可能是简单的length方法只是扩展到新的类型,如下所示:

Base.length(m::Mixture) = length(m.components) 

,现在你可以使用length(m),其中m是得到什么以前存储在K领域Mixture一个实例。

您的类型mixture中的内部构造函数不常见。标准做法是让内部构造函数将一对一(依次)与您的类型的字段相对应的参数,然后内部构造函数的其余部分仅执行任何错误检查,只要您初始化类型。你偏离了这个,因为qq不是(必然)是一个数组。这种行为更适合外部构造函数。那么,我用构造函数做了什么?

Mixture的内部构造函数除了通过new将参数传递给字段之外,没有其他任何操作。这是因为目前没有任何错误检查需要执行(但我可以在将来轻松添加一些错误)。

如果我想调用这个内部构造函数,我需要写一些像m = Mixture{MyType}(x),其中xVector{MyType}。这有点烦人。因此,我的第一个外部构造函数使用eltype(x)自动推断{...}的内容。因为我的第一个外部构造的,我可以用现在初始化Mixturem = Mixture(x)代替m = Mixture{MyType}(x)

我的第二个外部构造对应于你内心的构造函数。在我看来,您在此之后的行为是在components的每个字段中重复使用Mixture,并重复使用K次。所以我通过x来完成循环理解,只要定义了deepcopy方法就可以工作。如果不存在deepcopy方法,则会出现No Method Exists错误。这种编程称为鸭子打字,在Julia中,使用它通常没有性能损失。

请注意,我的第二个外部构造函数会调用我的第一个外部构造函数K次,并且每次我的第一个外部构造函数都会调用我的内部构造函数。在更复杂的情况下,以这种方式嵌套功能将大量减少代码重复。

对不起,这是我知道的很多东西。希望能帮助到你。

+0

谢谢您的完整回复。我很熟悉多次调度的概念,但我不确定最好的方法是修改Base.deepcopy。 也感谢您的意见。关于第三条评论,并且避免了在构造函数中声明类型的需要,尽管我设法使用了你的方法,但是我不太明白它! – Adham

+0

@Adham我已经更新了我的答案。如果现在还不清楚,让我知道,我会再试一次:-) –

+0

谢谢。现在更清楚了(对我来说,作为Julia的初学者:D) – Adham