03.31 我愛Julia之入門-038(參數方法設計樣式)

參數方法設計樣式

雖然複雜的分派邏輯對於性能或者可用性並不是必須的,但是有時這是表達某些算法的最好的方法。 這裡有一些常見的設計樣式,在以這個方法使用分派時可能會出現。

從超類型中提取出類型參數

這裡是一個正確的代碼模板,它返回 AbstractArray 的任意子類型的元素類型 T :

 \tabstract type AbstractArray{T, N} end
\teltype(::Type{<:abstractarray where="" t=""/>

使用了所謂的三角分派。注意如果 T 是一個 UnionAll 類型,比如 eltype(Array{T} where T <: integer="" any="" base="" eltype="">

我愛Julia之入門-038(參數方法設計樣式)

另外一個方法,這是在Julia v0.6中的三角分派到來之前的唯一正確方法。

\tabstract type AbstractArray{T, N} end
\teltype(::Type{AbstractArray}) = Any
\teltype(::Type{AbstractArray{T}}) where {T} = T
\teltype(::Type{AbstractArray{T, N}}) where {T, N} = T
\teltype(::Type{A}) where {A<:abstractarray eltype=""/>

另外一個可能性如下例,這可以對適配那些參數T需要更嚴格匹配的情況有用。

\teltype(::Type{AbstractArray{T, N} where {T<:s n="" where="" s="" any="">\teltype(::Type{AbstractArray{T, N} where {T<:s where="" s="" any="">\teltype(::Type{AbstractArray{T, N} where {N<:m where="" t="">\teltype(::Type{AbstractArray{T, N}}) where {T, N} = T
\teltype(::Type{A}) where {A <: abstractarray="" eltype=""/>

用不同的類型參數構建相似的類型

當構建通用代碼時,通常需要創建一些類似對象,在類型的佈局上有一些變化,這就也讓類型參數的變化變得必要。 例如,有一些任意元素類型的抽象數組,想使用特定的元素類型來編寫基於它的計算。這時候必須為每個 AbstractArray{T} 的子類型實現相應的方法,這些方法描述瞭如何進行類型轉換。我們不可能實現一個從一個子類型轉化成擁有一個不同參數的另一個子類型的通用方法。

AbstractArray 的子類型通常會實現兩個方法來完成這種需要: 一個方法把輸入轉換成特定的 AbstractArray{T,N} 抽象類型的子類型;一個方法用特定的元素類型構建一個新的未初始化的數組。這些的樣例實現可以在Julia Base裡面找到。這裡是一個基礎的樣例使用,保證輸入與輸出是同一種類型:

 input = convert(AbstractArray{Eltype}, input)
output = similar(input, Eltype)

作為這個的擴展,在算法需要輸入數組的拷貝的情況下,convert使無法勝任的,因為返回值可能只是原始輸入的別名。把similar(構建輸出數組)和copyto!(用輸入數據填滿)結合起來是需要給出輸入參數的可變拷貝的一個範用方法:

\tcopy_with_eltype(input, Eltype) = copyto!(similar(input, Eltype), input)

迭代分派

為了分派一個多層的參數列表,將每一層分派分開到不同的函數中常常是最好的。這可能聽起來跟單分派的方法相似,但是你會在下面見到,這個更加靈活。

例如,嘗試按照數組的元素類型進行分派常常會引起歧義。相反地,常見的代碼會首先按照容易類型分派,然後基於 eltype 遞歸到更加專用的方法。在大部分情況下,算法會很方便地就屈從於這個分層方法,在其他情況下,這種嚴苛的工作必須手動解決。例如在兩個矩陣的加法的邏輯中:

\t# 首先分派選擇了逐元素相加的map算法。
\t+(a::Matrix, b::Matrix) = map(+, a, b)
\t# 然後分派處理每個元素,然後選擇了恰當的元素類型進行提升。
+(a, b) = +(promote(a, b)...)
# 一旦元素有了相同類型,它們就可以相加。
# 例如,通過處理器暴露出的原始運算。
+(a::Float64, b::Float64) = Core.add(a, b)


分享到:


相關文章: