2017-02-07 30 views
4

我希望能夠創建一個用戶定義類型的調度,它基本上會做一個就地副本。不過,我想以類型穩定的方式來做,因此我想避免直接使用getfield,而是嘗試使用生成的函數。是否有可能爲一類像使用生成的函數生成類型穩定的`getfield`調用

type UserType{T} 
    x::Vector{T} 
    y::Vector{T} 
    z::T 
end 

生成一些功能

recursivecopy!(A::UserType,B::UserType) 
    # Do it for x 
    if typeof(A.x) <: AbstractArray 
    recursivecopy!(A.x,B.x) 
    else 
    A.x = B.x 
    end 
    # Now for y 
    if typeof(A.y) <: AbstractArray 
    recursivecopy!(A.y,B.y) 
    else 
    A.y = B.y 
    end 
    # Now for z 
    if typeof(A.z) <: AbstractArray 
    recursivecopy!(A.z,B.z) 
    else 
    A.z = B.z 
    end 
end 

在RecursiveArrayTools.jl的recursivecopy!使得這種處理嵌套(Vector{Vector})類型的好,但唯一的問題是,我不只需在編譯時調用該函數就可以預先知道用戶將要使用的字段。聽起來像生成函數的工作,但我不太清楚如何生成這個。

回答

5

您不需要向後彎曲以避免getfieldsetfield。朱莉婭可以推斷他們很好。當Julia無法確定它正在訪問哪個字段時,就會遇到麻煩......就像在for循環中一樣。

所以,唯一特別的事情所產生的功能需要做的是有效地展開與常量循環拼接成getfield

julia> immutable A 
      x::Int 
      y::Float64 
     end 

julia> @generated function f(x) 
      args = [:(getfield(x, $i)) for i=1:nfields(x)] 
      :(tuple($(args...))) 
     end 
f (generic function with 1 method) 

julia> f(A(1,2.4)) 
(1,2.4) 

julia> @code_warntype f(A(1,2.4)) 
Variables: 
    #self#::#f 
    x::A 

Body: 
    begin # line 2: 
     return (Main.tuple)((Main.getfield)(x::A,1)::Int64,(Main.getfield)(x::A,2)::Float64)::Tuple{Int64,Float64} 
    end::Tuple{Int64,Float64} 

就像你可以在多個參數拼接到一個函數調用,你也可以直接將多個表達式拼接到函數體。

julia> type B 
      x::Int 
      y::Float64 
     end 
julia> @generated function f!{T}(dest::T, src::T) 
      assignments = [:(setfield!(dest, $i, getfield(src, $i))) for i=1:nfields(T)] 
      :($(assignments...); dest) 
     end 
f! (generic function with 1 method) 

julia> f!(B(0,0), B(1, 2.4)) 
B(1,2.4) 

julia> @code_warntype f!(B(0,0), B(1, 2.4)) 
Variables: 
    #self#::#f! 
    dest::B 
    src::B 

Body: 
    begin # line 2: 
     (Main.setfield!)(dest::B,1,(Main.getfield)(src::B,1)::Int64)::Int64 
     (Main.setfield!)(dest::B,2,(Main.getfield)(src::B,2)::Float64)::Float64 
     return dest::B 
    end::B 

你當然可以使理解的主體像你想的那樣複雜。這有效地成爲您的for循環的內部。將數組放入函數的主體中會爲您展開。

+0

但是,你不能遍歷'args'數組,對吧?然後我需要遍歷它並檢查'typeof(args [i])',並且用'args [i]'替換''''調用,但那麼這不會再被推斷嗎?我需要以某種方式使用笛卡爾? –

+0

看我的編輯。你絕對不需要使用Base.Cartesian ......我給出的兩個例子基本上分別是'@ ncall'和'@ nexprs'。我覺得拼接更加平易近人,而且它也更具普遍性。 –

+0

這工作很好。謝謝。 –