2015-06-17 24 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