Julia 使用生成的函数生成类型稳定的`getfield`调用

Julia 使用生成的函数生成类型稳定的`getfield`调用,julia,Julia,我希望能够为用户定义的类型创建一个分派,该类型基本上将执行一个就地复制。但是,我希望以类型稳定的方式进行操作,因此我希望避免直接使用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) <: Abstract

我希望能够为用户定义的类型创建一个分派,该类型基本上将执行一个就地复制。但是,我希望以类型稳定的方式进行操作,因此我希望避免直接使用
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
recursivecopy!(A::UserType,B::UserType)
#为x做这件事

如果typeof(A.x)您不需要向后弯曲以避免
getfield
setfield
。朱莉娅可以很好地推断出来。当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
循环的内部。将数组分散到函数体中会为您展开。

但是您不能在
args
数组上循环,对吗?然后我需要在它上面循环并检查
typeof(args[I])
,然后用
args[I]
替换
A.
调用,但是这样就不能再推断了吗?我需要使用笛卡尔坐标吗?请参阅我的编辑。您肯定不需要使用Base.Cartesian…我给出的两个示例基本上分别是
@ncall
@nexprs
。我发现剪接更加平易近人……而且它也更具普遍性。