Julia 为什么我们需要'iteratoreltype()`?

Julia 为什么我们需要'iteratoreltype()`?,julia,Julia,我不知道为什么需要IteratorEltype()。每个iterable都保证生成类型为Any的对象,因此如果您不知道任何更好的方法,那么您可以始终默认使用该方法。区分“我知道它可能是任何东西”(IteratorEltype==HasEltype&&eltype=Any)和“我不知道它是什么,因此它可能是任何东西”(IteratorEltype==EltypeUnknown)有什么意义 在Base Julia中,我能找到的唯一使用EltypeUnknown()的类型是Generator,它是(i

我不知道为什么需要
IteratorEltype()
。每个iterable都保证生成类型为
Any
的对象,因此如果您不知道任何更好的方法,那么您可以始终默认使用该方法。区分“我知道它可能是任何东西”(
IteratorEltype==HasEltype&&eltype=Any
)和“我不知道它是什么,因此它可能是任何东西”(
IteratorEltype==EltypeUnknown
)有什么意义


在Base Julia中,我能找到的唯一使用
EltypeUnknown()
的类型是
Generator
,它是
(iter中的f(I)for I)
语法背后的工具。我可以想象,如果
eltype(iter)
是非叶型的,那么很难/不可能找到这样一个生成器的
eltype
,但是在这种情况下,不简单地将
eltype
设置为
Any
有什么好处呢?当然,如果
eltype(iter)
是叶子类型,而
f
是类型稳定的,那么您不希望它是
Any
,但这两种情况在编译时应该是可以区分的

这两个
IteratorEltype()
选项之间的区别如下所示:

  • HasEltype()
    表示迭代器的值应被视为类型为
    eltype()
    的对象,即使
    eltype()
    比迭代器中实际遇到的类型更一般

  • EltypeUnknown()
    要求像
    collect()
    这样的函数找出适用于所有元素的最具体类型


示例:

julia> abstract type Wrapper end
       Base.length(w::Wrapper) = length(w.data)
       Base.iterate(w::Wrapper, s...) = iterate(w.data, s...)

       struct EltypeWrapper{T,D} <: Wrapper
           data::D
       end
       EltypeWrapper{T}(data) where T = EltypeWrapper{T,typeof(data)}(data)
       Base.eltype(::Type{<:EltypeWrapper{T}}) where T = T

       struct EltypeUnknownWrapper{D} <: Wrapper
           data::D
       end
       Base.IteratorEltype(::Type{<:EltypeUnknownWrapper}) = Base.EltypeUnknown()

julia> collect(EltypeWrapper{Any}(Any[1,2.0]))
2-element Array{Any,1}:
 1  
 2.0

julia> collect(EltypeUnknownWrapper(Any[1,2.0]))
2-element Array{Real,1}:
 1  
 2.0

IteratorEltype()。如果对叶类型进行操作,几乎所有代码的速度都会快上一个数量级,因此在中可以更快地将
EltypeUnknown()
作为
IteratorEltype()
,并希望
eltype()
归结为具体类型,而不是设置
IteratorEltype()=HasEltype()
,然后设置
eltype()
到某个抽象类型。

base
目录中的快速
grep
显示
array.jl
reduce.jl
使用
iteratoreltype
。出于编译时优化和决策的原因。是的,但这种方法究竟能优化什么呢?
julia> using BenchmarkTools

       abstract type AbstractIterable end
       struct TypedIterable <: AbstractIterable; end
       struct UntypedIterable <: AbstractIterable; end
           
       Base.length(::AbstractIterable) = 100000

       Base.eltype(::Type{TypedIterable}) = Int
       Base.IteratorEltype(::Type{UntypedIterable}) = Base.EltypeUnknown()

       function Base.iterate(f::AbstractIterable,i = 1)
           i > length(f) && return nothing
           return i, i+1
       end

       @btime collect(UntypedIterable())
       @btime collect(TypedIterable())
       ;
  43.033 μs (2 allocations: 781.33 KiB)
  56.772 μs (2 allocations: 781.33 KiB)