diff --git a/base/iterators.jl b/base/iterators.jl index 9091430cfd894..1660c7ab1a0c4 100644 --- a/base/iterators.jl +++ b/base/iterators.jl @@ -426,28 +426,46 @@ julia> unzip([[1, "apple"], [2.5, "orange"], [0, "mango"]]) ``` """ function unzip(itrs) - n = Base.haslength(itrs) ? length(itrs) : -1 - vecs = Vector[] - for itr in itrs - for (j, x) in enumerate(itr) - if length(vecs) < j - v = [x] - push!(vecs, v) - n ≥ 0 && sizehint!(v, n) - else - v = vecs[j] - if !(x isa eltype(v)) - T = Base.promote_typejoin(typeof(x), eltype(v)) - v = vecs[j] = copyto!(similar(v, T), v) - n ≥ 0 && sizehint!(v, n) + n = Base.haslength(itrs) ? length(itrs) : nothing + outer = iterate(itrs) + outer === nothing && return () + vals, state = outer + vecs = ntuple(length(vals)) do i + x = vals[i] + v = Vector{typeof(x)}(undef, something(n, 1)) + @inbounds v[1] = x + return v + end + unzip_rest(vecs, typeof(vals), n isa Int ? 1 : nothing, itrs, state) +end + +function unzip_rest(vecs, eltypes, i, itrs, state) + while true + i isa Int && (i += 1) + outer = iterate(itrs, state) + outer === nothing && return vecs + itr, state = outer + vals = Tuple(itr) + if vals isa eltypes + for (v, x) in zip(vecs, vals) + if i isa Int + @inbounds v[i] = x + else + push!(v, x) end - push!(v, x) end + else + vecs′ = map(vecs, vals) do v, x + T = Base.promote_typejoin(eltype(v), typeof(x)) + v′ = Vector{T}(undef, length(v) + !(i isa Int)) + copyto!(v′, v) + @inbounds v′[something(i, end)] = x + return v′ + end + eltypes′ = Tuple{map(eltype, vecs′)...} + return unzip_rest(Tuple(vecs′), eltypes′, i, itrs, state) end - length(first(vecs)) == length(last(vecs)) || - throw(ArgumentError("unzip called with uneven iterators")) end - return Tuple(vecs) end # filter