2015-06-10 180 views
9

我想在我的函數定義中使用函數參數的子類型。這可能嗎?例如,我想編寫類似:我可以在函數定義中使用函數參數的子類型嗎?

g{T1, T2<:T1}(x::T1, y::T2) = x + y 

這樣g將任何x::T1進行限定,應當是T1亞型任何y。顯然,如果我知道,例如,T1將始終爲Number,那麼我可以寫g{T<:Number}(x::Number, y::T) = x + y,這將工作正常。但是這個問題是在運行時才知道T1的情況。

閱讀,如果你想知道爲什麼我會想這樣做:

的什麼,我試圖做的是有點麻煩,但接踵而來的是一個簡單的例子的完整描述。

我有一個參數化的類型,以及定義在該類型的簡單方法:

type MyVectorType{T} 
    x::Vector{T} 
end 
f1!{T}(m::MyVectorType{T}, xNew::T) = (m.x[1] = xNew) 

我也有另一種類型的,具有所定義的抽象超類型如下

abstract MyAbstract 
type MyType <: MyAbstract ; end 

創建MyVectorType的一個實例,其矢量元素類型設置爲MyAbstract使用:

m1 = MyVectorType(Array(MyAbstract, 1)) 

我現在想要在MyVectorType中放置MyType的實例。我可以做到這一點,因爲MyType <: MyAbstract。然而,我不能這樣做,因爲f1!,因爲功能定義意味着xNew必須是T類型,而T將是MyAbstract,而不是MyType

兩個解決方案,我能想到的這個問題是:

f2!(m::MyVectorType, xNew) = (m.x[1] = xNew) 
f3!{T1, T2}(m::MyVectorType{T1}, xNew::T2) = T2 <: T1 ? (m.x[1] = xNew) : error("Oh dear!") 

第本質上是一個鴨打字的解決方案。第二步在第一步中執行相應的錯誤檢查。

哪個是首選?還是有沒有第三個更好的解決方案,我不知道?

回答

11

定義函數g{T, S<:T}(::Vector{T}, ::S)的能力被稱爲「三角調度」,類似於對角線調度:f{T}(::Vector{T}, ::T)。 (想象一下,一個具有標記行和列的類型層次結構的表格,其排列方式使得超類型位於頂部和左側,行表示第一個參數的元素類型,列表示第二個參數的類型。只匹配沿着桌子對角線的單元格,而三角形調度匹配對角線及其下方的所有元素,形成一個三角形。)

這簡直還沒有實現 。這是一個複雜的問題,尤其是一旦您開始考慮函數定義之外和不變性環境中的TS的範圍。有關更多詳細信息,請參閱issue #3766#6984


所以,實際上,在這種情況下,我認爲鴨子打字就好。你依靠myVectorType的實現來執行錯誤檢查,當它分配它的元素時,它應該在任何情況下進行。

用於設置陣列的元件在鹼朱莉婭解決的辦法是這樣的:「三角」

f!{T}(A::Vector{T}, x::T) = (A[1] = x) 
f!{T}(A::Vector{T}, x) = f!(A, convert(T, x)) 

注意,它不擔心類型層次結構或子類型它只是試圖將x轉換爲T ......如果是x::S, S<:T,則這是不可操作的。如果convert不能進行轉換或者不知道如何,則會拋出一個錯誤。


UPDATE:這是現在實施的最新開發版本(0.6-DEV)!在這種情況下,我認爲我仍然推薦使用convert,就像我最初回答的一樣,但現在可以按照從左到右的方式在靜態方法參數中定義限制。

julia> f!{T1, T2<:T1}(A::Vector{T1}, x::T2) = "success!" 

julia> f!(Any[1,2,3], 4.) 
"success!" 

julia> f!(Integer[1,2,3], 4.) 
ERROR: MethodError: no method matching f!(::Array{Integer,1}, ::Float64) 
Closest candidates are: 
    f!{T1,T2<:T1}(::Array{T1,1}, ::T2<:T1) at REPL[1]:1 

julia> f!([1.,2.,3.], 4.) 
"success!" 
+1

非常有用的答案 - 我學到了很多。非常感謝。 –

相關問題