函数向输入参数添加维度时的类型稳定性问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了函数向输入参数添加维度时的类型稳定性问题相关的知识,希望对你有一定的参考价值。

我有一个函数返回一个与输入数组相同的元素类型的数组,但有一个额外的维度。这是一个简单的例子:

function myfun(a::Array{T,N}) where {T,N}
   b = Array{T,N+1}(size(a)...,2)
   b[:] = 42
   return b
end

在2x2阵列上调用此函数时,它返回一个2x2x2数组。

myfun(zeros(2,2))
2×2×2 Array{Float64,3}:
[:, :, 1] =
 42.0  42.0
 42.0  42.0

[:, :, 2] =
 42.0  42.0
 42.0  42.0

但是,此功能不是类型稳定的。根据@code_warntypebAny类型。

即使在b上使用了类型注释,结果在维数方面也不是类型稳定的:

function myfun(a::Array{T,N}) where {T,N}
      b = Array{T,N+1}(size(a)...,2) :: Array{T,N+1}
      b[:] = T(42)
      return b
end

@code_warntype myfun(zeros(2,2))现在返回Array{Float64,_} where _b类型。当输入参数有2个维度时,Julia是否应该无法确定维数为3?

我正在使用julia 0.6.2(在linux上)。

答案

这是因为构造函数(Array{T,N+1}(size(a)...,2))在运行时执行,你可以使用@generated functions在编译时预先计算N

julia> @generated function myfun(a::Array{T,N}) where {T,N}
           NN = N+1
           quote 
               b = Array{$T,$NN}(size(a)...,2)
               b[:] = 42
               return b
           end
       end
myfun (generic function with 1 method)

julia> @code_warntype myfun(zeros(2,2))
Variables:
  #self# <optimized out>
  a::Array{Float64,2}
  b::Array{Float64,3}

Body:
  begin  # line 2:
      # meta: location REPL[1] # line 4:
      SSAValue(2) = (Base.arraysize)(a::Array{Float64,2}, 1)::Int64
      SSAValue(1) = (Base.arraysize)(a::Array{Float64,2}, 2)::Int64
      b::Array{Float64,3} = $(Expr(:foreigncall, :(:jl_alloc_array_3d), Array{Float64,3}, svec(Any, Int64, Int64, Int64), Array{Float64,3}, 0, SSAValue(2), 0, SSAValue(1), 0, :($(QuoteNode(2))), 0)) # line 5:
      $(Expr(:invoke, MethodInstance for fill!(::Array{Float64,3}, ::Int64), :(Base.fill!), :(b), 42))
      # meta: pop location
      return b::Array{Float64,3}
  end::Array{Float64,3}

julia> myfun(zeros(2,2))
2×2×2 Array{Float64,3}:
[:, :, 1] =
 42.0  42.0
 42.0  42.0

[:, :, 2] =
 42.0  42.0
 42.0  42.0

以上是关于函数向输入参数添加维度时的类型稳定性问题的主要内容,如果未能解决你的问题,请参考以下文章

使用隐藏的输入类型向表单操作添加参数

添加片段时的 FlyOut 菜单设计问题

使用类型提示时如何向函数添加默认参数?

26 父组件向子组件(动静态)传值,参数为函数时的用法

是否有标准或首选标签类型可用于向某个位置添加额外的锚点/片段链接?

Python 可变类型作为函数默认参数时的副作用