Saya tidak yakin saya mengerti mengapa IteratorEltype() diperlukan. Setiap iterable dijamin menghasilkan objek bertipe Any, jadi jika Anda tidak tahu lebih baik maka Anda selalu dapat default untuk itu. Apa gunanya membedakan antara "Saya tahu itu bisa apa saja" (IteratorEltype == HasEltype && eltype = Any) dan "Saya tidak tahu apa itu, maka itu bisa apa saja" (IteratorEltype == EltypeUnknown)?

Satu-satunya jenis di Base Julia yang menggunakan EltypeUnknown() yang dapat saya temukan adalah Generator, yang merupakan alat di balik sintaks (f(i) for i in iter). Saya dapat membayangkan bahwa sulit / tidak mungkin untuk menemukan eltype dari generator seperti itu jika eltype(iter) adalah tipe non-daun, tetapi apa keuntungan dari tidak hanya mengatur eltype ke Any dalam kasus ini? Tentu saja, Anda tidak menginginkannya menjadi Any jika eltype(iter) adalah tipe daun dan f adalah tipe stabil, tetapi kedua kasus tersebut harus dapat dibedakan pada waktu kompilasi.

3
gTcV 22 November 2017, 18:58

1 menjawab

Jawaban Terbaik

Perbedaan antara kedua opsi IteratorEltype() adalah sebagai berikut:

  • HasEltype() mengatakan bahwa nilai iterator harus diperlakukan sebagai objek bertipe eltype(), bahkan jika eltype() lebih umum daripada tipe yang sebenarnya ditemui di iterator.

  • EltypeUnknown() meminta fungsi seperti collect() untuk mengetahui tipe paling spesifik yang berlaku untuk semua elemen.


Contoh:

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

Perhatikan bahwa kedua array memiliki entri yang sama, tetapi yang pertama memiliki tipe Vector{Any} sedangkan yang kedua memiliki tipe Vector{Real}.


Tampaknya HasEltype() seharusnya lebih efisien karena memungkinkan pra-alokasi output dalam fungsi seperti collect(). Julia cukup pandai menyimpulkan iterator eltype, dan EltypeUnknown() bisa secepat (atau untuk beberapa alasan aneh bahkan lebih cepat dari) HasEltype():

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)

IteratorEltype() dengan demikian tidak selalu berdampak pada kinerja bekerja dengan iterator, tetapi mungkin sangat berdampak pada kinerja lebih lanjut di tumpukan panggilan. Hampir semua kode urutan besarnya lebih cepat jika beroperasi pada jenis daun, sehingga dapat lebih cepat untuk menempatkan EltypeUnknown() sebagai IteratorEltype() dan berharap eltype() bermuara pada jenis konkret daripada menyetel IteratorEltype() = HasEltype() dan kemudian menyetel eltype() ke beberapa tipe abstrak.

0
gTcV 29 Oktober 2020, 07:23