如何获得Julia复合类型的深层副本?

时间:2015-06-17 01:19:53

标签: 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个组件都指的是一个对象并且操纵混合物。组件[k]将影响所有K个组件。在python中,我可以使用deepcopy来解决这个问题。但是Julia中的深度复制没有为复合类型定义。我该如何解决这个问题?

1 个答案:

答案 0 :(得分:13)

回答您的具体问题:

在Julia中定义新类型时,通常会将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),其中mMixture的一个实例,可以获取之前存储在K字段中的内容。

类型mixture中的内部构造函数不常见。标准做法是让内部构造函数接受与您的类型的字段一对一(按顺序)对应的参数,然后内部构造函数的其余部分只是执行任何错误检查,只要初始化您希望执行类型。你偏离了这个,因为qq不是(必然)数组。这种行为最好保留给外部构造函数。那么,我对构造函数做了什么?

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

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

我的第二个外部构造函数对应于您的内部构造函数。在我看来,您在此处的行为是在Mixture的每个字段中使用相同的组件初始化components,重复K次。所以我通过对x的循环理解来做到这一点,只要为deepcopy定义了x方法,它就会起作用。如果不存在deepcopy方法,则会出现No Method Exists错误。这种编程称为duck-typing,在Julia中使用它通常没有性能损失。

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

对不起,我知道这很重要。希望它有所帮助。