对 Julia 中的类型声明感到困惑

Iva*_*sas 3 performance struct types julia union-types

我正在尝试定义一个结构NodeLocator

@with_kw mutable struct NodeLocator{T<:Int64}
    length::T = 0
    value::Vector{T} = [0]
    maxtreelength::T = 0 end
Run Code Online (Sandbox Code Playgroud)

而且没有任何问题。

然而,我认为如果我这样做的话我的定义会更清晰

@with_kw mutable struct NodeLocator{T<:Union{Int64, Vector{Int64}}} # changed definition of T
    length::T = 0
    value::T = [0] # changed type declaration here
    maxtreelength::T = 0 end
#since
julia> Vector{Int64} <: Union{Int64,Vector{Int64}}
true
Run Code Online (Sandbox Code Playgroud)

但在这种情况下,我在构造这种类型的对象时遇到错误:

错误:MethodError:没有方法匹配 NodeLocator(::Int64, ::Vector{Int64}, ::Int64) 最接近的候选者是: NodeLocator(::T, !Matched::T, ::T) 其中 T<:Union{ Int64,矢量 {Int64}} 位于 C:\Users\ivica.julia\packages\Parameters\MK0O4\src\Parameters.jl:526

Int64对于上下文,它们的构造包括将s按元素添加到NodeLocator.value,我认为发生的情况是,在第一步之后,该字段的类型被锁定Int64并且无法更改为Vector{Int64}

如何参数化 的类型声明而NodeLocator.value不会遇到此错误?- 这样做有意义吗?我的主要问题是计算时间,因此性能对我来说非常重要。

cbk*_*cbk 5

看来您正在使用@with_kwParameters包中的宏,所以让我们从加载该包开始:

julia> using Parameters
Run Code Online (Sandbox Code Playgroud)

现在,复制您的定义

julia> @with_kw mutable struct NodeLocator{T<:Union{Int64, Vector{Int64}}}
           length::T = 0
           value::T = [0] # changed type declaration here
           maxtreelength::T = 0 end
NodeLocator
Run Code Online (Sandbox Code Playgroud)

我们可以看到,如果我们构造一个包含所有整数或所有向量的结构体,则效果很好

julia> NodeLocator(0,0,0)
NodeLocator{Int64}
  length: Int64 0
  value: Int64 0
  maxtreelength: Int64 0

julia> NodeLocator([0],[0],[0])
NodeLocator{Vector{Int64}}
  length: Array{Int64}((1,)) [0]
  value: Array{Int64}((1,)) [0]
  maxtreelength: Array{Int64}((1,)) [0]
Run Code Online (Sandbox Code Playgroud)

但对于混合物失败,包括您指定的默认值(实际上,(0, [0], 0)

julia> NodeLocator([0],[0],0)
ERROR: MethodError: no method matching NodeLocator(::Vector{Int64}, ::Vector{Int64}, ::Int64)
Closest candidates are:
  NodeLocator(::T, ::T, ::T) where T<:Union{Int64, Vector{Int64}} at ~/.julia/packages/Parameters/MK0O4/src/Parameters.jl:526
Stacktrace:
 [1] top-level scope
   @ REPL[38]:1

julia> NodeLocator() # use defaults
ERROR: MethodError: no method matching NodeLocator(::Int64, ::Vector{Int64}, ::Int64)
Closest candidates are:
  NodeLocator(::T, ::T, ::T) where T<:Union{Int64, Vector{Int64}} at ~/.julia/packages/Parameters/MK0O4/src/Parameters.jl:526
Stacktrace:
 [1] NodeLocator(; length::Int64, value::Vector{Int64}, maxtreelength::Int64)
   @ Main ~/.julia/packages/Parameters/MK0O4/src/Parameters.jl:545
 [2] NodeLocator()
   @ Main ~/.julia/packages/Parameters/MK0O4/src/Parameters.jl:545
 [3] top-level scope
   @ REPL[39]:1
Run Code Online (Sandbox Code Playgroud)

这是因为正如所写的,您已强制结构的每个字段具有相同的类型,T而该类型又是联合的子类型。

如果您确实希望每种类型都可以是 a Union,您可以编写如下内容:

julia> @with_kw mutable struct NodeLocator{T<:Int64}
           length::Union{T,Vector{T}} = 0
           value::Union{T,Vector{T}} = [0] # changed type declaration here
           maxtreelength::Union{T,Vector{T}} = 0 end

julia> NodeLocator() # use defaults
NodeLocator{Int64}
  length: Int64 0
  value: Array{Int64}((1,)) [0]
  maxtreelength: Int64 0
Run Code Online (Sandbox Code Playgroud)

但是,除非您确实需要所有三个字段都具有联合的灵活性,否则您最初的定义实际上更可取。Union在不需要的地方添加s 只会减慢速度。

如果您可以使用不可变的结构,将使代码更干净、更快的一件事是。即使您一开始可能没有意识到,这通常也是可能的,因为Array不可变内部的anstruct本身仍然是可变的,因此您可以交换该数组中的元素,而不需要整个结构都是可变的。但在某些情况下,您确实需要可变性——如果您确实需要,那就去做吧。

最后,我应该指出,限制数字类型不一定有多大意义,除非T<:Int64这确实是您需要的唯一数字类型。您可以通过编写类似T<:Integer或 之类的内容轻松地使您的代码更加通用T<:Number,这通常不会产生任何不利的性能成本。