From a2ce2309863564aed3fba4f8927d586b11983219 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Wed, 17 Feb 2016 15:41:56 -0500 Subject: [PATCH 1/3] make `map` and `collect` more general and uniform by adding iterator traits make HasEltype and HasLength the default this is a possible fix for #15342 type-accumulating Dict constructor some tests for type accumulation in `map` and `Dict` --- base/abstractarray.jl | 152 ++------------------------------------ base/array.jl | 100 ++++++++++++++++++++----- base/channels.jl | 2 + base/dict.jl | 45 +++++++---- base/essentials.jl | 3 + base/generator.jl | 37 +++++++++- base/intset.jl | 2 - base/io.jl | 2 + base/iterator.jl | 54 ++++++++++---- base/libgit2/reference.jl | 2 + base/libgit2/walker.jl | 2 + base/multidimensional.jl | 11 ++- base/regex.jl | 1 + base/set.jl | 1 + base/task.jl | 2 + test/abstractarray.jl | 22 +++--- test/arrayops.jl | 2 +- test/datafmt.jl | 2 +- test/dict.jl | 10 +++ test/functional.jl | 3 + test/simdloop.jl | 8 +- 21 files changed, 243 insertions(+), 220 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index f3a7d2f588968..68e85512e6df9 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -1086,34 +1086,6 @@ foreach(f) = (f(); nothing) foreach(f, itr) = (for x in itr; f(x); end; nothing) foreach(f, itrs...) = (for z in zip(itrs...); f(z...); end; nothing) -# generic map on any iterator -function map(f, iters...) - result = [] - len = length(iters) - states = [start(iters[idx]) for idx in 1:len] - nxtvals = cell(len) - cont = true - for idx in 1:len - if done(iters[idx], states[idx]) - cont = false - break - end - end - while cont - for idx in 1:len - nxtvals[idx],states[idx] = next(iters[idx], states[idx]) - end - push!(result, f(nxtvals...)) - for idx in 1:len - if done(iters[idx], states[idx]) - cont = false - break - end - end - end - result -end - ## map over arrays ## ## transform any set of dimensions @@ -1174,39 +1146,6 @@ function mapslices(f, A::AbstractArray, dims::AbstractVector) return R end - -# using promote_type -function promote_to!{T,F}(f::F, offs, dest::AbstractArray{T}, A::AbstractArray) - # map to dest array, checking the type of each result. if a result does not - # match, do a type promotion and re-dispatch. - for i = offs:length(A) #Fixme iter - @inbounds Ai = A[i] - el = f(Ai) - S = typeof(el) - if S === T || S <: T - @inbounds dest[i] = el::T - else - R = promote_type(T, S) - if R !== T - new = similar(dest, R) - copy!(new,1, dest,1, i-1) - new[i] = el - return promote_to!(f, i+1, new, A) - end - @inbounds dest[i] = el - end - end - return dest -end - -function map_promote(f, A::AbstractArray) - if isempty(A); return similar(A, Bottom); end - first = f(A[1]) - dest = similar(A, typeof(first)) - dest[1] = first - return promote_to!(f, 2, dest, A) -end - # These are needed because map(eltype, As) is not inferrable promote_eltype_op(::Any) = (@_pure_meta; Bottom) promote_eltype_op{T}(op, ::AbstractArray{T}) = (@_pure_meta; promote_op(op, T)) @@ -1225,39 +1164,11 @@ function map!{F}(f::F, dest::AbstractArray, A::AbstractArray) return dest end -function map_to!{T,F}(f::F, offs, st, dest::AbstractArray{T}, A) - # map to dest array, checking the type of each result. if a result does not - # match, widen the result type and re-dispatch. - i = offs - while !done(A, st) - @inbounds Ai, st = next(A, st) - el = f(Ai) - S = typeof(el) - if S === T || S <: T - @inbounds dest[i] = el::T - i += 1 - else - R = typejoin(T, S) - new = similar(dest, R) - copy!(new,1, dest,1, i-1) - @inbounds new[i] = el - return map_to!(f, i+1, st, new, A) - end - end - return dest -end +# map on collections +map(f, A::Union{AbstractArray,AbstractSet,Associative}) = collect_similar(A, Generator(f,A)) -function map(f, A::AbstractArray) - if isempty(A) - return isa(f,Type) ? similar(A,f) : similar(A) - end - st = start(A) - A1, st = next(A, st) - first = f(A1) - dest = similar(A, typeof(first)) - dest[1] = first - return map_to!(f, 2, st, dest, A) -end +# default to returning an Array for `map` on general iterators +map(f, A) = collect(Generator(f,A)) ## 2 argument function map!{F}(f::F, dest::AbstractArray, A::AbstractArray, B::AbstractArray) @@ -1267,34 +1178,6 @@ function map!{F}(f::F, dest::AbstractArray, A::AbstractArray, B::AbstractArray) return dest end -function map_to!{T,F}(f::F, offs, dest::AbstractArray{T}, A::AbstractArray, B::AbstractArray) - for i = offs:length(A) #Fixme iter - @inbounds Ai, Bi = A[i], B[i] - el = f(Ai, Bi) - S = typeof(el) - if (S !== T) && !(S <: T) - R = typejoin(T, S) - new = similar(dest, R) - copy!(new,1, dest,1, i-1) - @inbounds new[i] = el - return map_to!(f, i+1, new, A, B) - end - @inbounds dest[i] = el::T - end - return dest -end - -function map(f, A::AbstractArray, B::AbstractArray) - shp = promote_shape(size(A),size(B)) - if prod(shp) == 0 - return similar(A, promote_type(eltype(A),eltype(B)), shp) - end - first = f(A[1], B[1]) - dest = similar(A, typeof(first), shp) - dest[1] = first - return map_to!(f, 2, dest, A, B) -end - ## N argument ith_all(i, ::Tuple{}) = () @@ -1310,32 +1193,7 @@ end map!{F}(f::F, dest::AbstractArray, As::AbstractArray...) = map_n!(f, dest, As) -function map_to_n!{T,F}(f::F, offs, dest::AbstractArray{T}, As) - for i = offs:length(As[1]) - el = f(ith_all(i, As)...) - S = typeof(el) - if (S !== T) && !(S <: T) - R = typejoin(T, S) - new = similar(dest, R) - copy!(new,1, dest,1, i-1) - @inbounds new[i] = el - return map_to_n!(f, i+1, new, As) - end - @inbounds dest[i] = el::T - end - return dest -end - -function map(f, As::AbstractArray...) - shape = mapreduce(size, promote_shape, As) - if prod(shape) == 0 - return similar(As[1], promote_eltype(As...), shape) - end - first = f(map(a->a[1], As)...) - dest = similar(As[1], typeof(first), shape) - dest[1] = first - return map_to_n!(f, 2, dest, As) -end +map(f, iters...) = collect(Generator(f, iters...)) # multi-item push!, unshift! (built on top of type-specific 1-item version) # (note: must not cause a dispatch loop when 1-item case is not defined) diff --git a/base/array.jl b/base/array.jl index f64c43c89ad87..0737eedc8445d 100644 --- a/base/array.jl +++ b/base/array.jl @@ -199,35 +199,99 @@ convert{T,n,S}(::Type{Array{T,n}}, x::AbstractArray{S,n}) = copy!(Array(T, size( promote_rule{T,n,S}(::Type{Array{T,n}}, ::Type{Array{S,n}}) = Array{promote_type(T,S),n} +## copying iterators to containers + +# make a collection similar to `c` and appropriate for collecting `itr` +_similar_for(c::AbstractArray, T, itr, ::SizeUnknown) = similar(c, T, 0) +_similar_for(c::AbstractArray, T, itr, ::HasLength) = similar(c, T, Int(length(itr)::Integer)) +_similar_for(c::AbstractArray, T, itr, ::HasShape) = similar(c, T, convert(Dims,size(itr))) +_similar_for(c, T, itr, isz) = similar(c, T) + """ collect(element_type, collection) Return an array of type `Array{element_type,1}` of all items in a collection. """ -function collect{T}(::Type{T}, itr) - if applicable(length, itr) - # when length() isn't defined this branch might pollute the - # type of the other. - a = Array(T,length(itr)::Integer) - i = 0 - for x in itr - a[i+=1] = x - end - else - a = Array(T,0) - for x in itr - push!(a,x) - end - end - return a -end +collect{T}(::Type{T}, itr) = collect(Generator(T, itr)) """ collect(collection) Return an array of all items in a collection. For associative collections, returns Pair{KeyType, ValType}. """ -collect(itr) = collect(eltype(itr), itr) +collect(itr) = _collect(1:1 #= Array =#, itr, iteratoreltype(itr), iteratorsize(itr)) + +collect_similar(cont, itr) = _collect(cont, itr, iteratoreltype(itr), iteratorsize(itr)) + +_collect(cont, itr, ::HasEltype, isz::Union{HasLength,HasShape}) = + copy!(_similar_for(cont, eltype(itr), itr, isz), itr) + +function _collect(cont, itr, ::HasEltype, isz::SizeUnknown) + a = _similar_for(cont, eltype(itr), itr, isz) + for x in itr + push!(a,x) + end + return a +end + +_collect(c, itr, ::EltypeUnknown, isz::SizeUnknown) = grow_to!(_similar_for(c, Union{}, itr, isz), itr) + +function _collect(c, itr, ::EltypeUnknown, isz::Union{HasLength,HasShape}) + st = start(itr) + if done(itr,st) + return _similar_for(c, Union{}, itr, isz) + end + v1, st = next(itr, st) + collect_to_with_first!(_similar_for(c, typeof(v1), itr, isz), v1, itr, st) +end + +function collect_to_with_first!(dest::AbstractArray, v1, itr, st) + dest[1] = v1 + return collect_to!(dest, itr, 2, st) +end + +function collect_to_with_first!(dest, v1, itr, st) + push!(dest, v1) + return grow_to!(dest, itr, st) +end + +function collect_to!{T}(dest::AbstractArray{T}, itr, offs, st) + # collect to dest array, checking the type of each result. if a result does not + # match, widen the result type and re-dispatch. + i = offs + while !done(itr, st) + el, st = next(itr, st) + S = typeof(el) + if S === T || S <: T + @inbounds dest[i] = el::T + i += 1 + else + R = typejoin(T, S) + new = similar(dest, R) + copy!(new,1, dest,1, i-1) + @inbounds new[i] = el + return collect_to!(new, itr, i+1, st) + end + end + return dest +end + +function grow_to!(dest, itr, st = start(itr)) + T = eltype(dest) + while !done(itr, st) + el, st = next(itr, st) + S = typeof(el) + if S === T || S <: T + push!(dest, el::T) + else + new = similar(dest, typejoin(T, S)) + copy!(new, dest) + push!(new, el) + return grow_to!(new, itr, st) + end + end + return dest +end ## Iteration ## start(A::Array) = 1 diff --git a/base/channels.jl b/base/channels.jl index 34778b1585b3a..2362b5742a900 100644 --- a/base/channels.jl +++ b/base/channels.jl @@ -95,3 +95,5 @@ function done(c::Channel, state::Ref) end end next{T}(c::Channel{T}, state) = (v=get(state[]); state[]=nothing; (v, state)) + +iteratorsize{C<:Channel}(::Type{C}) = SizeUnknown() diff --git a/base/dict.jl b/base/dict.jl index c3d4ee24a8745..38b7fce79492a 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -2,8 +2,6 @@ # generic operations on associative collections -abstract Associative{K,V} - const secret_table_token = :__c782dbf1cf4d6a2e5e3865d7e95634f2e09b5902__ haskey(d::Associative, k) = in(k,keys(d)) @@ -225,6 +223,16 @@ function merge!(d::Associative, others::Associative...) end return d end + +# very similar to `merge!`, but accepts any iterable and extends code +# that would otherwise only use `copy!` with arrays. +function copy!(dest::Union{Associative,AbstractSet}, src) + for x in src + push!(dest, x) + end + return dest +end + keytype{K,V}(::Type{Associative{K,V}}) = K keytype(a::Associative) = keytype(typeof(a)) keytype{A<:Associative}(::Type{A}) = keytype(supertype(A)) @@ -449,19 +457,6 @@ copy(d::Dict) = Dict(d) const AnyDict = Dict{Any,Any} -# TODO: this can probably be simplified using `eltype` as a THT (Tim Holy trait) -Dict{K,V}(kv::Tuple{Vararg{Tuple{K,V}}}) = Dict{K,V}(kv) -Dict{K }(kv::Tuple{Vararg{Tuple{K,Any}}}) = Dict{K,Any}(kv) -Dict{V }(kv::Tuple{Vararg{Tuple{Any,V}}}) = Dict{Any,V}(kv) -Dict{K,V}(kv::Tuple{Vararg{Pair{K,V}}}) = Dict{K,V}(kv) -Dict{K }(kv::Tuple{Vararg{Pair{K}}}) = Dict{K,Any}(kv) -Dict{V }(kv::Tuple{Vararg{Pair{TypeVar(:K),V}}}) = Dict{Any,V}(kv) -Dict( kv::Tuple{Vararg{Pair}}) = Dict{Any,Any}(kv) - -Dict{K,V}(kv::AbstractArray{Tuple{K,V}}) = Dict{K,V}(kv) -Dict{K,V}(kv::AbstractArray{Pair{K,V}}) = Dict{K,V}(kv) -Dict{K,V}(kv::Associative{K,V}) = Dict{K,V}(kv) - Dict{K,V}(ps::Pair{K,V}...) = Dict{K,V}(ps) Dict{K }(ps::Pair{K}...,) = Dict{K,Any}(ps) Dict{V }(ps::Pair{TypeVar(:K),V}...,) = Dict{Any,V}(ps) @@ -482,9 +477,27 @@ end dict_with_eltype{K,V}(kv, ::Type{Tuple{K,V}}) = Dict{K,V}(kv) dict_with_eltype{K,V}(kv, ::Type{Pair{K,V}}) = Dict{K,V}(kv) -dict_with_eltype(kv, t) = Dict{Any,Any}(kv) +dict_with_eltype(kv, t) = grow_to!(Dict{Union{},Union{}}(), kv) + +# this is a special case due to (1) allowing both Pairs and Tuples as elements, +# and (2) Pair being invariant. a bit annoying. +function grow_to!{K,V}(dest::Associative{K,V}, itr, st = start(itr)) + while !done(itr, st) + (k,v), st = next(itr, st) + if isa(k,K) && isa(v,V) + dest[k] = v + else + new = similar(dest, Pair{typejoin(K,typeof(k)), typejoin(V,typeof(v))}) + copy!(new, dest) + new[k] = v + return grow_to!(new, itr, st) + end + end + return dest +end similar{K,V}(d::Dict{K,V}) = Dict{K,V}() +similar{K,V}(d::Dict, ::Type{Pair{K,V}}) = Dict{K,V}() # conversion between Dict types function convert{K,V}(::Type{Dict{K,V}},d::Associative) diff --git a/base/essentials.jl b/base/essentials.jl index 7d42dad0efdbb..059fe58431c15 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -6,6 +6,9 @@ typealias Callable Union{Function,DataType} const Bottom = Union{} +abstract AbstractSet{T} +abstract Associative{K,V} + # The real @inline macro is not available until after array.jl, so this # internal macro splices the meta Expr directly into the function body. macro _inline_meta() diff --git a/base/generator.jl b/base/generator.jl index dff08ea9d2542..92918b4d0816b 100644 --- a/base/generator.jl +++ b/base/generator.jl @@ -13,6 +13,8 @@ end Generator(f, c1, c...) = Generator(a->f(a...), zip(c1, c...)) +Generator{T,I}(::Type{T}, iter::I) = Generator{I,Type{T}}(T, iter) + start(g::Generator) = start(g.iter) done(g::Generator, s) = done(g.iter, s) function next(g::Generator, s) @@ -20,4 +22,37 @@ function next(g::Generator, s) g.f(v), s2 end -collect(g::Generator) = map(g.f, g.iter) +## iterator traits + +abstract IteratorSize +immutable SizeUnknown <: IteratorSize end +immutable HasLength <: IteratorSize end +immutable HasShape <: IteratorSize end +immutable IsInfinite <: IteratorSize end + +iteratorsize(x) = iteratorsize(typeof(x)) +iteratorsize(::Type) = HasLength() # HasLength is the default + +and_iteratorsize{T}(isz::T, ::T) = isz +and_iteratorsize(::HasLength, ::HasShape) = HasLength() +and_iteratorsize(::HasShape, ::HasLength) = HasLength() +and_iteratorsize(a, b) = SizeUnknown() + +abstract IteratorEltype +immutable EltypeUnknown <: IteratorEltype end +immutable HasEltype <: IteratorEltype end + +iteratoreltype(x) = iteratoreltype(typeof(x)) +iteratoreltype(::Type) = HasEltype() # HasEltype is the default + +and_iteratoreltype{T}(iel::T, ::T) = iel +and_iteratoreltype(a, b) = EltypeUnknown() + +iteratorsize{T<:AbstractArray}(::Type{T}) = HasShape() +iteratorsize{I,F}(::Type{Generator{I,F}}) = iteratorsize(I) +length(g::Generator) = length(g.iter) +size(g::Generator) = size(g.iter) + +iteratoreltype{I,T}(::Type{Generator{I,T}}) = EltypeUnknown() +iteratoreltype{I,T}(::Type{Generator{I,Type{T}}}) = HasEltype() +eltype{I,T}(::Type{Generator{I,Type{T}}}) = T diff --git a/base/intset.jl b/base/intset.jl index 5b610eae563c7..89f862d90f9da 100644 --- a/base/intset.jl +++ b/base/intset.jl @@ -1,7 +1,5 @@ # This file is a part of Julia. License is MIT: http://julialang.org/license -abstract AbstractSet{T} - type IntSet <: AbstractSet{Int} bits::Array{UInt32,1} limit::Int diff --git a/base/io.jl b/base/io.jl index ada3d3bd4e28d..4019df0c4a514 100644 --- a/base/io.jl +++ b/base/io.jl @@ -378,6 +378,8 @@ eltype(::Type{EachLine}) = ByteString readlines(s=STDIN) = collect(eachline(s)) +iteratorsize(::Type{EachLine}) = SizeUnknown() + # IOStream Marking # Note that these functions expect that io.mark exists for # the concrete IO type. This may not be true for IO types diff --git a/base/iterator.jl b/base/iterator.jl index ee8337795c3c9..a9f13665b9fc5 100644 --- a/base/iterator.jl +++ b/base/iterator.jl @@ -10,6 +10,7 @@ end enumerate(itr) = Enumerate(itr) length(e::Enumerate) = length(e.itr) +size(e::Enumerate) = size(e.itr) start(e::Enumerate) = (1, start(e.itr)) function next(e::Enumerate, state) n = next(e.itr,state[2]) @@ -19,6 +20,9 @@ done(e::Enumerate, state) = done(e.itr, state[2]) eltype{I}(::Type{Enumerate{I}}) = Tuple{Int, eltype(I)} +iteratorsize{I}(::Type{Enumerate{I}}) = iteratorsize(I) +iteratoreltype{I}(::Type{Enumerate{I}}) = iteratoreltype(I) + # zip abstract AbstractZipIterator @@ -28,6 +32,7 @@ immutable Zip1{I} <: AbstractZipIterator end zip(a) = Zip1(a) length(z::Zip1) = length(z.a) +size(z::Zip1) = size(z.a) eltype{I}(::Type{Zip1{I}}) = Tuple{eltype(I)} @inline start(z::Zip1) = (start(z.a),) @inline function next(z::Zip1, st) @@ -36,12 +41,16 @@ eltype{I}(::Type{Zip1{I}}) = Tuple{eltype(I)} end @inline done(z::Zip1, st) = done(z.a,st[1]) +iteratorsize{I}(::Type{Zip1{I}}) = iteratorsize(I) +iteratoreltype{I}(::Type{Zip1{I}}) = iteratoreltype(I) + immutable Zip2{I1, I2} <: AbstractZipIterator a::I1 b::I2 end zip(a, b) = Zip2(a, b) length(z::Zip2) = min(length(z.a), length(z.b)) +size(z::Zip2) = promote_shape(size(z.a), size(z.b)) eltype{I1,I2}(::Type{Zip2{I1,I2}}) = Tuple{eltype(I1), eltype(I2)} @inline start(z::Zip2) = (start(z.a), start(z.b)) @inline function next(z::Zip2, st) @@ -51,12 +60,16 @@ eltype{I1,I2}(::Type{Zip2{I1,I2}}) = Tuple{eltype(I1), eltype(I2)} end @inline done(z::Zip2, st) = done(z.a,st[1]) | done(z.b,st[2]) +iteratorsize{I1,I2}(::Type{Zip2{I1,I2}}) = and_iteratorsize(iteratorsize(I1),iteratorsize(I2)) +iteratoreltype{I1,I2}(::Type{Zip2{I1,I2}}) = and_iteratoreltype(iteratoreltype(I1),iteratoreltype(I2)) + immutable Zip{I, Z<:AbstractZipIterator} <: AbstractZipIterator a::I z::Z end zip(a, b, c...) = Zip(a, zip(b, c...)) length(z::Zip) = min(length(z.a), length(z.z)) +size(z::Zip) = promote_shape(size(z.a), size(z.z)) tuple_type_cons{S}(::Type{S}, ::Type{Union{}}) = Union{} function tuple_type_cons{S,T<:Tuple}(::Type{S}, ::Type{T}) @_pure_meta @@ -71,6 +84,9 @@ eltype{I,Z}(::Type{Zip{I,Z}}) = tuple_type_cons(eltype(I), eltype(Z)) end @inline done(z::Zip, st) = done(z.a,st[1]) | done(z.z,st[2]) +iteratorsize{I1,I2}(::Type{Zip{I1,I2}}) = and_iteratorsize(iteratorsize(I1),iteratorsize(I2)) +iteratoreltype{I1,I2}(::Type{Zip{I1,I2}}) = and_iteratoreltype(iteratoreltype(I1),iteratoreltype(I2)) + # filter immutable Filter{F,I} @@ -108,6 +124,8 @@ end done(f::Filter, s) = s[1] eltype{I}(::Type{Filter{I}}) = eltype(I) +iteratoreltype{F,I}(::Type{Filter{F,I}}) = iteratoreltype(I) +iteratorsize{T<:Filter}(::Type{T}) = SizeUnknown() # Rest -- iterate starting at the given state @@ -122,6 +140,8 @@ next(i::Rest, st) = next(i.itr, st) done(i::Rest, st) = done(i.itr, st) eltype{I}(::Type{Rest{I}}) = eltype(I) +iteratoreltype{I,S}(::Type{Rest{I,S}}) = iteratoreltype(I) +iteratorsize{T<:Rest}(::Type{T}) = SizeUnknown() # Count -- infinite counting @@ -139,6 +159,8 @@ start(it::Count) = it.start next(it::Count, state) = (state, state + it.step) done(it::Count, state) = false +iteratorsize{S}(::Type{Count{S}}) = IsInfinite() + # Take -- iterate through the first n elements immutable Take{I} @@ -148,6 +170,8 @@ end take(xs, n::Int) = Take(xs, n) eltype{I}(::Type{Take{I}}) = eltype(I) +iteratoreltype{I}(::Type{Take{I}}) = iteratoreltype(I) +iteratorsize{T<:Take}(::Type{T}) = SizeUnknown() # TODO start(it::Take) = (it.n, start(it.xs)) @@ -171,6 +195,8 @@ end drop(xs, n::Int) = Drop(xs, n) eltype{I}(::Type{Drop{I}}) = eltype(I) +iteratoreltype{I}(::Type{Drop{I}}) = iteratoreltype(I) +iteratorsize{T<:Drop}(::Type{T}) = SizeUnknown() # TODO function start(it::Drop) xs_state = start(it.xs) @@ -195,6 +221,8 @@ end cycle(xs) = Cycle(xs) eltype{I}(::Type{Cycle{I}}) = eltype(I) +iteratoreltype{I}(::Type{Cycle{I}}) = iteratoreltype(I) +iteratorsize{I}(::Type{Cycle{I}}) = IsInfinite() function start(it::Cycle) s = start(it.xs) @@ -219,6 +247,7 @@ immutable Repeated{O} end repeated(x) = Repeated(x) eltype{O}(::Type{Repeated{O}}) = O +iteratorsize{O}(::Type{Repeated{O}}) = IsInfinite() start(it::Repeated) = nothing next(it::Repeated, state) = (it.x, nothing) done(it::Repeated, state) = false @@ -253,7 +282,9 @@ changes the fastest. Example: product(a) = Zip1(a) product(a, b) = Prod2(a, b) eltype{I1,I2}(::Type{Prod2{I1,I2}}) = Tuple{eltype(I1), eltype(I2)} +iteratoreltype{I1,I2}(::Type{Prod2{I1,I2}}) = and_iteratoreltype(iteratoreltype(I1),iteratoreltype(I2)) length(p::AbstractProdIterator) = length(p.a)*length(p.b) +iteratorsize{I1,I2}(::Type{Prod2{I1,I2}}) = prod_iteratorsize(iteratorsize(I1),iteratorsize(I2)) function start(p::AbstractProdIterator) s1, s2 = start(p.a), start(p.b) @@ -287,12 +318,17 @@ end product(a, b, c...) = Prod(a, product(b, c...)) eltype{I1,I2}(::Type{Prod{I1,I2}}) = tuple_type_cons(eltype(I1), eltype(I2)) +iteratoreltype{I1,I2}(::Type{Prod{I1,I2}}) = and_iteratoreltype(iteratoreltype(I1),iteratoreltype(I2)) +iteratorsize{I1,I2}(::Type{Prod{I1,I2}}) = prod_iteratorsize(iteratorsize(I1),iteratorsize(I2)) @inline function next{I1,I2}(p::Prod{I1,I2}, st) x = prod_next(p, st) ((x[1][1],x[1][2]...), x[2]) end +prod_iteratorsize(::Union{HasLength,HasShape}, ::Union{HasLength,HasShape}) = HasLength() +prod_iteratorsize(a, b) = SizeUnknown() + _size(p::Prod2) = (length(p.a), length(p.b)) _size(p::Prod) = (length(p.a), _size(p.b)...) @@ -325,22 +361,10 @@ next(i::IteratorND, s) = next(i.iter, s) size(i::IteratorND) = i.dims length(i::IteratorND) = prod(size(i)) ndims{I,N}(::IteratorND{I,N}) = N +iteratorsize{T<:IteratorND}(::Type{T}) = HasShape() eltype{I}(::IteratorND{I}) = eltype(I) - -collect(i::IteratorND) = copy!(Array(eltype(i),size(i)), i) - -function collect{I<:IteratorND}(g::Generator{I}) - sz = size(g.iter) - if length(g.iter) == 0 - return Array(Union{}, sz) - end - st = start(g) - first, st = next(g, st) - dest = Array(typeof(first), sz) - dest[1] = first - return map_to!(g.f, 2, st, dest, g.iter) -end +iteratoreltype{I}(::Type{IteratorND{I}}) = iteratoreltype(I) # flatten an iterator of iterators @@ -365,6 +389,8 @@ Put differently, the elements of the argument iterator are concatenated. Example flatten(itr) = Flatten(itr) eltype{I}(::Type{Flatten{I}}) = eltype(eltype(I)) +iteratorsize{I}(::Type{Flatten{I}}) = SizeUnknown() +iteratoreltype{I}(::Type{Flatten{I}}) = iteratoreltype(eltype(I)) function start(f::Flatten) local inner, s2 diff --git a/base/libgit2/reference.jl b/base/libgit2/reference.jl index 38081e3634492..23119c1478baa 100644 --- a/base/libgit2/reference.jl +++ b/base/libgit2/reference.jl @@ -181,6 +181,8 @@ function Base.next(bi::GitBranchIter, state) return (state[1:2], (GitReference(ref_ptr_ptr[]), btype[1], false)) end +Base.iteratorsize(::Type{GitBranchIter}) = Base.SizeUnknown() + function Base.map(f::Function, bi::GitBranchIter) res = nothing s = start(bi) diff --git a/base/libgit2/walker.jl b/base/libgit2/walker.jl index 9bf2fa93529df..1d564c3c406db 100644 --- a/base/libgit2/walker.jl +++ b/base/libgit2/walker.jl @@ -25,6 +25,8 @@ function Base.next(w::GitRevWalker, state) return (state[1], (id_ptr[], false)) end +Base.iteratorsize(::Type{GitRevWalker}) = Base.SizeUnknown() + function push_head!(w::GitRevWalker) @check ccall((:git_revwalk_push_head, :libgit2), Cint, (Ptr{Void},), w.ptr) return w diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 8b1fe4f18952c..d3fd6d1b7df1d 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -3,7 +3,7 @@ ### Multidimensional iterators module IteratorsMD -import Base: eltype, length, start, done, next, last, getindex, setindex!, linearindexing, min, max, eachindex, ndims +import Base: eltype, length, size, start, done, next, last, getindex, setindex!, linearindexing, min, max, eachindex, ndims, iteratorsize importall ..Base.Operators import Base: simd_outer_range, simd_inner_length, simd_index, @generated import Base: @nref, @ncall, @nif, @nexprs, LinearFast, LinearSlow, to_index, AbstractCartesianIndex @@ -110,6 +110,7 @@ end end eltype{I}(::Type{CartesianRange{I}}) = I +iteratorsize{I}(::Type{CartesianRange{I}}) = Base.HasShape() @generated function start{I<:CartesianIndex}(iter::CartesianRange{I}) N = length(I) @@ -142,13 +143,15 @@ start{I<:CartesianIndex{0}}(iter::CartesianRange{I}) = false next{I<:CartesianIndex{0}}(iter::CartesianRange{I}, state) = iter.start, true done{I<:CartesianIndex{0}}(iter::CartesianRange{I}, state) = state -@generated function length{I<:CartesianIndex}(iter::CartesianRange{I}) +@generated function size{I<:CartesianIndex}(iter::CartesianRange{I}) N = length(I) - N == 0 && return 1 + N == 0 && return () args = [:(iter.stop[$i]-iter.start[$i]+1) for i=1:N] - Expr(:call,:*,args...) + Expr(:tuple,args...) end +length(iter::CartesianRange) = prod(size(iter)) + last(iter::CartesianRange) = iter.stop @generated function simd_outer_range{I}(iter::CartesianRange{I}) diff --git a/base/regex.jl b/base/regex.jl index 5898349717c85..711df7a207f08 100644 --- a/base/regex.jl +++ b/base/regex.jl @@ -327,6 +327,7 @@ compile(itr::RegexMatchIterator) = (compile(itr.regex); itr) eltype(::Type{RegexMatchIterator}) = RegexMatch start(itr::RegexMatchIterator) = match(itr.regex, itr.string, 1, UInt32(0)) done(itr::RegexMatchIterator, prev_match) = (prev_match === nothing) +iteratorsize(::Type{RegexMatchIterator}) = SizeUnknown() # Assumes prev_match is not nothing function next(itr::RegexMatchIterator, prev_match) diff --git a/base/set.jl b/base/set.jl index 46a14aa73e279..d4ec130cd0c96 100644 --- a/base/set.jl +++ b/base/set.jl @@ -11,6 +11,7 @@ Set(itr) = Set{eltype(itr)}(itr) eltype{T}(::Type{Set{T}}) = T similar{T}(s::Set{T}) = Set{T}() +similar(s::Set, T::Type) = Set{T}() function show(io::IO, s::Set) print(io,"Set") diff --git a/base/task.jl b/base/task.jl index a674fa611a5ea..060fb794273d9 100644 --- a/base/task.jl +++ b/base/task.jl @@ -238,6 +238,8 @@ function done(t::Task, val) istaskdone(t) end next(t::Task, val) = (t.result, nothing) +iteratorsize(::Type{Task}) = SizeUnknown() +iteratoreltype(::Type{Task}) = EltypeUnknown() isempty(::Task) = error("isempty not defined for Tasks") diff --git a/test/abstractarray.jl b/test/abstractarray.jl index 9148bc5c7b0bc..e2a2a2769de13 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -385,6 +385,7 @@ type GenericIterator{N} end Base.start{N}(::GenericIterator{N}) = 1 Base.next{N}(::GenericIterator{N}, i) = (i, i + 1) Base.done{N}(::GenericIterator{N}, i) = i > N ? true : false +Base.iteratorsize{N}(::Type{GenericIterator{N}}) = Base.SizeUnknown() function test_map(::Type{TestAbstractArray}) @@ -409,29 +410,27 @@ function test_map(::Type{TestAbstractArray}) # AbstractArray map for 2 arg case f(x, y) = x + y - A = Array(Int, 10) B = Float64[1:10...] C = Float64[1:10...] - @test Base.map_to!(f, 1, A, B, C) == Real[ 2 * i for i in 1:10 ] - @test map(f, Int[], Float64[]) == Float64[] + @test map(f, convert(Vector{Int},B), C) == Float64[ 2 * i for i in 1:10 ] + @test map(f, Int[], Float64[]) == Union{}[] @test collect(Base.StreamMapIterator(f, Int[], Float64[])) == Float64[] + # map with different result tyoes + let m = map(x->x+1, Number[1, 2.0]) + @test isa(m, Vector{Real}) + @test m == Real[2, 3.0] + end # AbstractArray map for N-arg case + A = Array(Int, 10) f(x, y, z) = x + y + z D = Float64[1:10...] @test map!(f, A, B, C, D) == Int[ 3 * i for i in 1:10 ] - @test Base.map_to_n!(f, 1, A, (B, C, D)) == Real[ 3 * i for i in 1:10 ] @test map(f, B, C, D) == Float64[ 3 * i for i in 1:10 ] @test collect(Base.StreamMapIterator(f, B, C, D)) == Float64[ 3 * i for i in 1:10 ] - @test map(f, Int[], Int[], Complex{Int}[]) == Number[] @test collect(Base.StreamMapIterator(f, Int[], Int[], Complex{Int}[])) == Number[] -end - -function test_map_promote(::Type{TestAbstractArray}) - A = [1:10...] - f(x) = iseven(x) ? 1.0 : 1 - @test Base.map_promote(f, A) == fill(1.0, 10) + @test map(f, Int[], Int[], Complex{Int}[]) == Union{}[] end function test_UInt_indexing(::Type{TestAbstractArray}) @@ -496,7 +495,6 @@ test_get(TestAbstractArray) test_cat(TestAbstractArray) test_ind2sub(TestAbstractArray) test_map(TestAbstractArray) -test_map_promote(TestAbstractArray) test_UInt_indexing(TestAbstractArray) test_vcat_depwarn(TestAbstractArray) test_13315(TestAbstractArray) diff --git a/test/arrayops.jl b/test/arrayops.jl index c0a95d5afc96b..f2ee8ab894265 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -1464,7 +1464,7 @@ let A = zeros(Int, 2, 2), B = zeros(Float64, 2, 2) end # issue #14482 -@inferred Base.map_to!(Int8, 1, 1, Int8[0], Int[0]) +@inferred map(Int8, Int[0]) # make sure @inbounds isn't used too much type OOB_Functor{T}; a::T; end diff --git a/test/datafmt.jl b/test/datafmt.jl index 8e7cba730e8ce..fe32b81f6408e 100644 --- a/test/datafmt.jl +++ b/test/datafmt.jl @@ -259,7 +259,7 @@ for writefunc in ((io,x) -> writemime(io, "text/csv", x), let x = ["foo", "bar"], io = IOBuffer() writefunc(io, x) seek(io, 0) - @test collect(readcsv(io)) == x + @test vec(readcsv(io)) == x end end diff --git a/test/dict.jl b/test/dict.jl index 08d799b8e00e3..4116844a6cc94 100644 --- a/test/dict.jl +++ b/test/dict.jl @@ -137,6 +137,16 @@ end @test_throws ArgumentError first(Dict()) @test first(Dict(:f=>2)) == (:f=>2) +# constructing Dicts from iterators +let d = Dict(i=>i for i=1:3) + @test isa(d, Dict{Int,Int}) + @test d == Dict(1=>1, 2=>2, 3=>3) +end +let d = Dict(i==1 ? (1=>2) : (2.0=>3.0) for i=1:2) + @test isa(d, Dict{Real,Real}) + @test d == Dict{Real,Real}(2.0=>3.0, 1=>2) +end + # issue #1821 let d = Dict{UTF8String, Vector{Int}}() diff --git a/test/functional.jl b/test/functional.jl index 0c813f8366e6a..050bf9272c8b9 100644 --- a/test/functional.jl +++ b/test/functional.jl @@ -58,6 +58,9 @@ let z = zip(1:2, 3:4, 5:6) @test eltype(z) == Tuple{Int,Int,Int} end +# typed `collect` +@test collect(Float64, Filter(isodd, [1,2,3,4]))[1] === 1.0 + # enumerate (issue #6284) let b = IOBuffer("1\n2\n3\n"), a = [] for (i,x) in enumerate(eachline(b)) diff --git a/test/simdloop.jl b/test/simdloop.jl index 782c4e91f9cd6..771383aedf419 100644 --- a/test/simdloop.jl +++ b/test/simdloop.jl @@ -104,17 +104,17 @@ end crng = CartesianRange(CartesianIndex{4}(2,0,1,3), CartesianIndex{4}(4,1,1,5)) indexes = simd_cartesian_range!(Array(eltype(crng), 0), crng) -@test indexes == collect(crng) +@test indexes == vec(collect(crng)) crng = CartesianRange(CartesianIndex{2}(-1,1), CartesianIndex{2}(1,3)) indexes = simd_cartesian_range!(Array(eltype(crng), 0), crng) -@test indexes == collect(crng) +@test indexes == vec(collect(crng)) crng = CartesianRange(CartesianIndex{2}(-1,1), CartesianIndex{2}(-1,3)) indexes = simd_cartesian_range!(Array(eltype(crng), 0), crng) -@test indexes == collect(crng) +@test indexes == vec(collect(crng)) crng = CartesianRange(CartesianIndex{1}(2), CartesianIndex{1}(4)) @@ -124,7 +124,7 @@ indexes = simd_cartesian_range!(Array(eltype(crng), 0), crng) crng = CartesianRange(CartesianIndex{0}(), CartesianIndex{0}()) indexes = simd_cartesian_range!(Array(eltype(crng), 0), crng) -@test indexes == collect(crng) +@test indexes == vec(collect(crng)) # @simd with array as "range" # issue #13869 From d60e002c50e7e6bb0aa10bb3fb5231c529e0e3d5 Mon Sep 17 00:00:00 2001 From: mschauer Date: Tue, 8 Mar 2016 16:56:05 +0100 Subject: [PATCH 2/3] zips, products and takes with infinite components --- base/iterator.jl | 22 +++++++++++++++++----- test/functional.jl | 9 +++++++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/base/iterator.jl b/base/iterator.jl index a9f13665b9fc5..58a1849607ab7 100644 --- a/base/iterator.jl +++ b/base/iterator.jl @@ -27,6 +27,15 @@ iteratoreltype{I}(::Type{Enumerate{I}}) = iteratoreltype(I) abstract AbstractZipIterator +zip_iteratorsize(a, b) = and_iteratorsize(a,b) # as `and_iteratorsize` but inherit `Union{HasLength,IsInfinite}` of the shorter iterator +zip_iteratorsize(::HasLength, ::IsInfinite) = HasLength() +zip_iteratorsize(::HasShape, ::IsInfinite) = HasLength() +zip_iteratorsize(a::IsInfinite, b) = zip_iteratorsize(b,a) +_min_length(a, b, ::IsInfinite, ::IsInfinite) = min(length(a),length(b)) # inherit behaviour, error +_min_length(a, b, A, ::IsInfinite) = length(a) +_min_length(a, b, ::IsInfinite, B) = length(b) +_min_length(a, b, A, B) = min(length(a),length(b)) + immutable Zip1{I} <: AbstractZipIterator a::I end @@ -49,7 +58,7 @@ immutable Zip2{I1, I2} <: AbstractZipIterator b::I2 end zip(a, b) = Zip2(a, b) -length(z::Zip2) = min(length(z.a), length(z.b)) +length(z::Zip2) = _min_length(z.a, z.b, iteratorsize(z.a), iteratorsize(z.b)) size(z::Zip2) = promote_shape(size(z.a), size(z.b)) eltype{I1,I2}(::Type{Zip2{I1,I2}}) = Tuple{eltype(I1), eltype(I2)} @inline start(z::Zip2) = (start(z.a), start(z.b)) @@ -60,7 +69,7 @@ eltype{I1,I2}(::Type{Zip2{I1,I2}}) = Tuple{eltype(I1), eltype(I2)} end @inline done(z::Zip2, st) = done(z.a,st[1]) | done(z.b,st[2]) -iteratorsize{I1,I2}(::Type{Zip2{I1,I2}}) = and_iteratorsize(iteratorsize(I1),iteratorsize(I2)) +iteratorsize{I1,I2}(::Type{Zip2{I1,I2}}) = zip_iteratorsize(iteratorsize(I1),iteratorsize(I2)) iteratoreltype{I1,I2}(::Type{Zip2{I1,I2}}) = and_iteratoreltype(iteratoreltype(I1),iteratoreltype(I2)) immutable Zip{I, Z<:AbstractZipIterator} <: AbstractZipIterator @@ -68,7 +77,7 @@ immutable Zip{I, Z<:AbstractZipIterator} <: AbstractZipIterator z::Z end zip(a, b, c...) = Zip(a, zip(b, c...)) -length(z::Zip) = min(length(z.a), length(z.z)) +length(z::Zip) = _min_length(z.a, z.z, iteratorsize(z.a), iteratorsize(z.z)) size(z::Zip) = promote_shape(size(z.a), size(z.z)) tuple_type_cons{S}(::Type{S}, ::Type{Union{}}) = Union{} function tuple_type_cons{S,T<:Tuple}(::Type{S}, ::Type{T}) @@ -84,7 +93,7 @@ eltype{I,Z}(::Type{Zip{I,Z}}) = tuple_type_cons(eltype(I), eltype(Z)) end @inline done(z::Zip, st) = done(z.a,st[1]) | done(z.z,st[2]) -iteratorsize{I1,I2}(::Type{Zip{I1,I2}}) = and_iteratorsize(iteratorsize(I1),iteratorsize(I2)) +iteratorsize{I1,I2}(::Type{Zip{I1,I2}}) = zip_iteratorsize(iteratorsize(I1),iteratorsize(I2)) iteratoreltype{I1,I2}(::Type{Zip{I1,I2}}) = and_iteratoreltype(iteratoreltype(I1),iteratoreltype(I2)) # filter @@ -141,7 +150,9 @@ done(i::Rest, st) = done(i.itr, st) eltype{I}(::Type{Rest{I}}) = eltype(I) iteratoreltype{I,S}(::Type{Rest{I,S}}) = iteratoreltype(I) -iteratorsize{T<:Rest}(::Type{T}) = SizeUnknown() +rest_iteratorsize(a) = SizeUnknown() +rest_iteratorsize(::IsInfinite) = IsInfinite() +iteratorsize{I,S}(::Type{Rest{I,S}}) = rest_iteratorsize(iteratorsize(I)) # Count -- infinite counting @@ -327,6 +338,7 @@ iteratorsize{I1,I2}(::Type{Prod{I1,I2}}) = prod_iteratorsize(iteratorsize(I1),it end prod_iteratorsize(::Union{HasLength,HasShape}, ::Union{HasLength,HasShape}) = HasLength() +prod_iteratorsize(a, ::IsInfinite) = IsInfinite() # products can have an infinite last iterator (which moves slowest) prod_iteratorsize(a, b) = SizeUnknown() _size(p::Prod2) = (length(p.a), length(p.b)) diff --git a/test/functional.jl b/test/functional.jl index 050bf9272c8b9..e1a538d072109 100644 --- a/test/functional.jl +++ b/test/functional.jl @@ -79,6 +79,11 @@ let zeb = IOBuffer("1\n2\n3\n4\n5\n"), @test res == [(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e')] end +@test length(zip(cycle(1:3), 1:7)) == 7 +@test length(zip(cycle(1:3), 1:7, cycle(1:3))) == 7 +@test length(zip(1:3,Base.product(1:7,cycle(1:3)))) == 3 +@test length(zip(1:3,Base.product(1:7,cycle(1:3)),8)) == 1 + # rest # ---- let s = "hello" @@ -86,6 +91,8 @@ let s = "hello" @test collect(rest(s, st)) == ['e','l','l','o'] end +@test_throws MethodError collect(rest(countfrom(1), 5)) + # countfrom # --------- @@ -158,6 +165,7 @@ let i = 0 end end + # product # ------- @@ -168,6 +176,7 @@ end @test collect(Base.product(1:2,3:4)) == [(1,3),(2,3),(1,4),(2,4)] @test isempty(collect(Base.product(1:0,1:2))) @test length(Base.product(1:2,1:10,4:6)) == 60 +@test Base.iteratorsize(Base.product(1:2, countfrom(1))) == Base.IsInfinite() # flatten # ------- From bd02ba4d5ad4d4e86b54fdd307741512d92068c6 Mon Sep 17 00:00:00 2001 From: mschauer Date: Thu, 10 Mar 2016 16:01:13 +0100 Subject: [PATCH 3/3] length() of Take and Drop iterators add `iteratorsize(::Type{StreamMapIterator})` --- base/iterator.jl | 26 ++++++++++++++++++++------ base/mapiterator.jl | 2 ++ test/functional.jl | 10 +++++++++- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/base/iterator.jl b/base/iterator.jl index 58a1849607ab7..bb0229648f49c 100644 --- a/base/iterator.jl +++ b/base/iterator.jl @@ -2,6 +2,16 @@ isempty(itr) = done(itr, start(itr)) +_min_length(a, b, ::IsInfinite, ::IsInfinite) = min(length(a),length(b)) # inherit behaviour, error +_min_length(a, b, A, ::IsInfinite) = length(a) +_min_length(a, b, ::IsInfinite, B) = length(b) +_min_length(a, b, A, B) = min(length(a),length(b)) + +_diff_length(a, b, A, ::IsInfinite) = 0 +_diff_length(a, b, ::IsInfinite, ::IsInfinite) = 0 +_diff_length(a, b, ::IsInfinite, B) = length(a) # inherit behaviour, error +_diff_length(a, b, A, B) = max(length(a)-length(b), 0) + # enumerate immutable Enumerate{I} @@ -31,10 +41,7 @@ zip_iteratorsize(a, b) = and_iteratorsize(a,b) # as `and_iteratorsize` but inher zip_iteratorsize(::HasLength, ::IsInfinite) = HasLength() zip_iteratorsize(::HasShape, ::IsInfinite) = HasLength() zip_iteratorsize(a::IsInfinite, b) = zip_iteratorsize(b,a) -_min_length(a, b, ::IsInfinite, ::IsInfinite) = min(length(a),length(b)) # inherit behaviour, error -_min_length(a, b, A, ::IsInfinite) = length(a) -_min_length(a, b, ::IsInfinite, B) = length(b) -_min_length(a, b, A, B) = min(length(a),length(b)) + immutable Zip1{I} <: AbstractZipIterator a::I @@ -182,7 +189,10 @@ take(xs, n::Int) = Take(xs, n) eltype{I}(::Type{Take{I}}) = eltype(I) iteratoreltype{I}(::Type{Take{I}}) = iteratoreltype(I) -iteratorsize{T<:Take}(::Type{T}) = SizeUnknown() # TODO +take_iteratorsize(a) = HasLength() +take_iteratorsize(::SizeUnknown) = SizeUnknown() +iteratorsize{I}(::Type{Take{I}}) = take_iteratorsize(iteratorsize(I)) +length(t::Take) = _min_length(t.xs, 1:t.n, iteratorsize(t.xs), HasLength()) start(it::Take) = (it.n, start(it.xs)) @@ -207,7 +217,11 @@ drop(xs, n::Int) = Drop(xs, n) eltype{I}(::Type{Drop{I}}) = eltype(I) iteratoreltype{I}(::Type{Drop{I}}) = iteratoreltype(I) -iteratorsize{T<:Drop}(::Type{T}) = SizeUnknown() # TODO +drop_iteratorsize(::SizeUnknown) = SizeUnknown() +drop_iteratorsize(::Union{HasShape, HasLength}) = HasLength() +drop_iteratorsize(::IsInfinite) = IsInfinite() +iteratorsize{I}(::Type{Drop{I}}) = drop_iteratorsize(iteratorsize(I)) +length(d::Drop) = _diff_length(d.xs, 1:d.n, iteratorsize(d.xs), HasLength()) function start(it::Drop) xs_state = start(it.xs) diff --git a/base/mapiterator.jl b/base/mapiterator.jl index 75bde41ed6c85..edb64dabe8ff4 100644 --- a/base/mapiterator.jl +++ b/base/mapiterator.jl @@ -144,3 +144,5 @@ function next(itr::StreamMapIterator, state::StreamMapState) return (r, state) end + +iteratorsize(::Type{StreamMapIterator}) = SizeUnknown() diff --git a/test/functional.jl b/test/functional.jl index e1a538d072109..abc3279ffd343 100644 --- a/test/functional.jl +++ b/test/functional.jl @@ -91,7 +91,7 @@ let s = "hello" @test collect(rest(s, st)) == ['e','l','l','o'] end -@test_throws MethodError collect(rest(countfrom(1), 5)) +@test_throws MethodError collect(rest(countfrom(1), 5)) # countfrom # --------- @@ -125,6 +125,10 @@ let i = 0 @test i == 10 end +@test length(take(1:3,typemax(Int))) == 3 +@test length(take(countfrom(1),3)) == 3 +@test length(take(1:6,3)) == 3 + # drop # ---- @@ -136,6 +140,10 @@ let i = 0 @test i == 4 end +@test length(drop(1:3,typemax(Int))) == 0 +@test Base.iteratorsize(drop(countfrom(1),3)) == Base.IsInfinite() +@test_throws MethodError length(drop(countfrom(1), 3)) + # cycle # -----