From ce96a516b59e8ed4769da47bafb1417075c96e60 Mon Sep 17 00:00:00 2001 From: Davide Lasagna Date: Wed, 18 May 2016 23:48:09 +0100 Subject: [PATCH 1/4] Change iteratorsize trait of `product(itr1, itr2)` *) Fixes Issue #16436. *) Adds many tests to product function and tests more thoroughly the iterator traits *) Adds a Prod1 type *) Adds ndims(::Base.Prod*) *) Change state of Prod1 iterator from tuple to integer removed let block removed trailing whitespace changed state of Prod1 from tuple to integer changed eltype of iterators for more generality added ndims(::Base.Prod*) --- base/iterator.jl | 40 ++++++++++++-- test/functional.jl | 126 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 153 insertions(+), 13 deletions(-) diff --git a/base/iterator.jl b/base/iterator.jl index 498fc21f1fb9a..e45b4d760fd62 100644 --- a/base/iterator.jl +++ b/base/iterator.jl @@ -298,10 +298,33 @@ done(it::Repeated, state) = false repeated(x, n::Int) = take(repeated(x), n) -# product + +# Product -- cartesian product of iterators abstract AbstractProdIterator +length(p::AbstractProdIterator) = prod(size(p)) + +# one iterator +immutable Prod1{I} <: AbstractProdIterator + a::I +end +product(a) = Prod1(a) + +eltype{I}(::Type{Prod1{I}}) = Tuple{eltype(I)} +size(p::Prod1) = size(p.a) +ndims(p::Prod1) = ndims(p.a) + +@inline start(p::Prod1) = start(p.a) +@inline function next(p::Prod1, st) + n, st = next(p.a, st) + return (n, ), st +end +@inline done(p::Prod1, st) = done(p.a, st) +iteratoreltype{I}(::Type{Prod1{I}}) = iteratoreltype(I) +iteratorsize{I}(::Type{Prod1{I}}) = iteratorsize(I) + +# two iterators immutable Prod2{I1, I2} <: AbstractProdIterator a::I1 b::I2 @@ -323,11 +346,13 @@ changes the fastest. Example: (1,5) (2,5) """ -product(a) = Zip1(a) product(a, b) = Prod2(a, b) + eltype{I1,I2}(::Type{Prod2{I1,I2}}) = Tuple{eltype(I1), eltype(I2)} +size(p::Prod2) = (length(p.a), length(p.b)) +ndims(p::Prod2) = 2 + 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) @@ -355,13 +380,17 @@ end @inline next(p::Prod2, st) = prod_next(p, st) @inline done(p::AbstractProdIterator, st) = st[4] +# n iterators immutable Prod{I1, I2<:AbstractProdIterator} <: AbstractProdIterator a::I1 b::I2 end - product(a, b, c...) = Prod(a, product(b, c...)) + eltype{I1,I2}(::Type{Prod{I1,I2}}) = tuple_type_cons(eltype(I1), eltype(I2)) +size(p::Prod) = (length(p.a), size(p.b)...) +ndims(p::Prod) = length(size(p)) + 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)) @@ -370,8 +399,9 @@ iteratorsize{I1,I2}(::Type{Prod{I1,I2}}) = prod_iteratorsize(iteratorsize(I1),it ((x[1][1],x[1][2]...), x[2]) end -prod_iteratorsize(::Union{HasLength,HasShape}, ::Union{HasLength,HasShape}) = HasLength() +prod_iteratorsize(::Union{HasLength,HasShape}, ::Union{HasLength,HasShape}) = HasShape() prod_iteratorsize(a, ::IsInfinite) = IsInfinite() # products can have an infinite last iterator (which moves slowest) +prod_iteratorsize(::IsInfinite, b) = IsInfinite() 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 31e04885b93c0..3fd94dc413a00 100644 --- a/test/functional.jl +++ b/test/functional.jl @@ -177,14 +177,124 @@ end # product # ------- -@test isempty(Base.product(1:2,1:0)) -@test isempty(Base.product(1:2,1:0,1:10)) -@test isempty(Base.product(1:2,1:10,1:0)) -@test isempty(Base.product(1:0,1:2,1:10)) -@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() +# empty? +for itr in [Base.product(1:0), + Base.product(1:2, 1:0), + Base.product(1:0, 1:2), + Base.product(1:0, 1:1, 1:2), + Base.product(1:1, 1:0, 1:2), + Base.product(1:1, 1:2 ,1:0)] + @test isempty(itr) + @test isempty(collect(itr)) +end + +# collect a product - first iterators runs faster +@test collect(Base.product(1:2)) == [(i,) for i=1:2] +@test collect(Base.product(1:2, 3:4)) == [(i, j) for i=1:2, j=3:4] +@test collect(Base.product(1:2, 3:4, 5:6)) == [(i, j, k) for i=1:2, j=3:4, k=5:6] + +# iteration order +let expected = [(1,3,5), (2,3,5), (1,4,5), (2,4,5), (1,3,6), (2,3,6), (1,4,6), (2,4,6)] + i = 1 + for el in Base.product(1:2, 3:4, 5:6) + @test el == expected[i] + i+=1 + end +end + +# is this the correct behaviour? +@test collect(Base.product([1 2; 3 4], [5 6; 7 8])) == [(1,5) (1,7) (1,6) (1,8); + (3,5) (3,7) (3,6) (3,8); + (2,5) (2,7) (2,6) (2,8); + (4,5) (4,7) (4,6) (4,8)] + +let + a, b, c, d, e = 1:2, 1.0:10.0, 4f0:6f0, 0x01:0x08, Int8(1):Int8(0) + + # length + @test length(Base.product(a)) == 2 + @test length(Base.product(a, b)) == 20 + @test length(Base.product(a, b, c)) == 60 + @test length(Base.product(a, b, c, d)) == 480 + @test length(Base.product(a, b, c, d, e)) == 0 + + # size + @test size(Base.product(a)) == (2, ) + @test size(Base.product(a, b)) == (2, 10) + @test size(Base.product(a, b, c)) == (2, 10, 3) + @test size(Base.product(a, b, c, d)) == (2, 10, 3, 8) + @test size(Base.product(a, b, c, d, e)) == (2, 10, 3, 8, 0) + + # eltype + @test eltype(Base.product(a)) == Tuple{Int} + @test eltype(Base.product(a, b)) == Tuple{Int, Float64} + @test eltype(Base.product(a, b, c)) == Tuple{Int, Float64, Float32} + @test eltype(Base.product(a, b, c, d)) == Tuple{Int, Float64, Float32, UInt8} + @test eltype(Base.product(a, b, c, d, e)) == Tuple{Int, Float64, Float32, UInt8, Int8} + + # ndims + @test ndims(Base.product(a)) == 1 + @test ndims(Base.product(a, b)) == 2 + @test ndims(Base.product(a, b, c)) == 3 + @test ndims(Base.product(a, b, c, d)) == 4 + @test ndims(Base.product(a, b, c, d, e)) == 5 + + f, g, h = randn(1, 1), randn(1, 1, 1), randn(1, 1, 1, 1) + @test ndims(Base.product(f)) == ndims(collect(Base.product(f))) == 2 + @test ndims(Base.product(g)) == ndims(collect(Base.product(g))) == 3 + @test ndims(Base.product(h)) == ndims(collect(Base.product(h))) == 4 + @test ndims(Base.product(f, f)) == ndims(collect(Base.product(f, f))) == 2 + @test ndims(Base.product(f, g)) == ndims(collect(Base.product(f, g))) == 2 + @test ndims(Base.product(g, g)) == ndims(collect(Base.product(g, g))) == 2 + @test ndims(Base.product(g, h)) == ndims(collect(Base.product(g, h))) == 2 + @test ndims(Base.product(h, h)) == ndims(collect(Base.product(h, h))) == 2 + @test ndims(Base.product(f, f, f)) == ndims(collect(Base.product(f, f, f))) == 3 + @test ndims(Base.product(f, f, g)) == ndims(collect(Base.product(f, f, g))) == 3 + @test ndims(Base.product(f, g, g)) == ndims(collect(Base.product(f, g, g))) == 3 + @test ndims(Base.product(g, g, g)) == ndims(collect(Base.product(g, g, g))) == 3 + @test ndims(Base.product(g, g, h)) == ndims(collect(Base.product(g, g, h))) == 3 + @test ndims(Base.product(g, h, h)) == ndims(collect(Base.product(g, h, h))) == 3 + @test ndims(Base.product(h, h, h)) == ndims(collect(Base.product(h, h, h))) == 3 + @test ndims(Base.product(f, f, f)) == ndims(collect(Base.product(f, f, f))) == 3 + @test ndims(Base.product(g, g, g, g)) == ndims(collect(Base.product(g, g, g, g))) == 4 + @test ndims(Base.product(h, h, h, h)) == ndims(collect(Base.product(h, h, h, h))) == 4 +end + +# iteratorsize trait business +let f1 = Filter(i->i>0, 1:10) + @test Base.iteratorsize(Base.product(f1)) == Base.SizeUnknown() + @test Base.iteratorsize(Base.product(1:2, f1)) == Base.SizeUnknown() + @test Base.iteratorsize(Base.product(f1, 1:2)) == Base.SizeUnknown() + @test Base.iteratorsize(Base.product(f1, f1)) == Base.SizeUnknown() + @test Base.iteratorsize(Base.product(f1, countfrom(1))) == Base.IsInfinite() + @test Base.iteratorsize(Base.product(countfrom(1), f1)) == Base.IsInfinite() +end +@test Base.iteratorsize(Base.product(1:2, countfrom(1))) == Base.IsInfinite() +@test Base.iteratorsize(Base.product(countfrom(1), 1:2)) == Base.IsInfinite() +@test Base.iteratorsize(Base.product(1:2)) == Base.HasShape() +@test Base.iteratorsize(Base.product(1:2, 1:2)) == Base.HasShape() +@test Base.iteratorsize(Base.product(take(1:2, 1), take(1:2, 1))) == Base.HasShape() +@test Base.iteratorsize(Base.product(take(1:2, 2))) == Base.HasLength() +@test Base.iteratorsize(Base.product([1 2; 3 4])) == Base.HasShape() + +# iteratoreltype trait business +let f1 = Filter(i->i>0, 1:10) + @test Base.iteratoreltype(Base.product(f1)) == Base.HasEltype() # FIXME? eltype(f1) is Any + @test Base.iteratoreltype(Base.product(1:2, f1)) == Base.HasEltype() # FIXME? eltype(f1) is Any + @test Base.iteratoreltype(Base.product(f1, 1:2)) == Base.HasEltype() # FIXME? eltype(f1) is Any + @test Base.iteratoreltype(Base.product(f1, f1)) == Base.HasEltype() # FIXME? eltype(f1) is Any + @test Base.iteratoreltype(Base.product(f1, countfrom(1))) == Base.HasEltype() # FIXME? eltype(f1) is Any + @test Base.iteratoreltype(Base.product(countfrom(1), f1)) == Base.HasEltype() # FIXME? eltype(f1) is Any +end +@test Base.iteratoreltype(Base.product(1:2, countfrom(1))) == Base.HasEltype() +@test Base.iteratoreltype(Base.product(countfrom(1), 1:2)) == Base.HasEltype() +@test Base.iteratoreltype(Base.product(1:2)) == Base.HasEltype() +@test Base.iteratoreltype(Base.product(1:2, 1:2)) == Base.HasEltype() +@test Base.iteratoreltype(Base.product(take(1:2, 1), take(1:2, 1))) == Base.HasEltype() +@test Base.iteratoreltype(Base.product(take(1:2, 2))) == Base.HasEltype() +@test Base.iteratoreltype(Base.product([1 2; 3 4])) == Base.HasEltype() + + # flatten # ------- From 5c082ec4a7710a62be1d54fa3c5811d0c90860f8 Mon Sep 17 00:00:00 2001 From: Davide Lasagna Date: Sat, 21 May 2016 16:57:05 +0100 Subject: [PATCH 2/4] Improved behaviour for multi-dimensional arguments --- base/iterator.jl | 29 ++++++--- test/functional.jl | 157 ++++++++++++++++++++++++++++++--------------- 2 files changed, 126 insertions(+), 60 deletions(-) diff --git a/base/iterator.jl b/base/iterator.jl index e45b4d760fd62..627379b27485a 100644 --- a/base/iterator.jl +++ b/base/iterator.jl @@ -302,7 +302,24 @@ repeated(x, n::Int) = take(repeated(x), n) # Product -- cartesian product of iterators abstract AbstractProdIterator + length(p::AbstractProdIterator) = prod(size(p)) +size(p::AbstractProdIterator) = _prod_size(p.a, p.b, iteratorsize(p.a), iteratorsize(p.b)) +ndims(p::AbstractProdIterator) = length(size(p)) + +# generic methods to handle size of Prod* types +_prod_size(a, ::HasShape) = size(a) +_prod_size(a, ::HasLength) = (length(a), ) +_prod_size(a, A) = + throw(ArgumentError("Cannot compute size for object of type $(typeof(a))")) +_prod_size(a, b, ::HasLength, ::HasLength) = (length(a), length(b)) +_prod_size(a, b, ::HasLength, ::HasShape) = (length(a), size(b)...) +_prod_size(a, b, ::HasShape, ::HasLength) = (size(a)..., length(b)) +_prod_size(a, b, ::HasShape, ::HasShape) = (size(a)..., size(b)...) +_prod_size(a, b, A, ::Union{HasShape, HasLength}) = + throw(ArgumentError("Cannot compute size for object of type $(typeof(a))")) +_prod_size(a, b, ::Union{HasShape, HasLength}, B) = + throw(ArgumentError("Cannot compute size for object of type $(typeof(b))")) # one iterator immutable Prod1{I} <: AbstractProdIterator @@ -311,13 +328,12 @@ end product(a) = Prod1(a) eltype{I}(::Type{Prod1{I}}) = Tuple{eltype(I)} -size(p::Prod1) = size(p.a) -ndims(p::Prod1) = ndims(p.a) +size(p::Prod1) = _prod_size(p.a, iteratorsize(p.a)) @inline start(p::Prod1) = start(p.a) @inline function next(p::Prod1, st) n, st = next(p.a, st) - return (n, ), st + (n, ), st end @inline done(p::Prod1, st) = done(p.a, st) @@ -349,8 +365,6 @@ changes the fastest. Example: product(a, b) = Prod2(a, b) eltype{I1,I2}(::Type{Prod2{I1,I2}}) = Tuple{eltype(I1), eltype(I2)} -size(p::Prod2) = (length(p.a), length(p.b)) -ndims(p::Prod2) = 2 iteratoreltype{I1,I2}(::Type{Prod2{I1,I2}}) = and_iteratoreltype(iteratoreltype(I1),iteratoreltype(I2)) iteratorsize{I1,I2}(::Type{Prod2{I1,I2}}) = prod_iteratorsize(iteratorsize(I1),iteratorsize(I2)) @@ -388,8 +402,6 @@ end product(a, b, c...) = Prod(a, product(b, c...)) eltype{I1,I2}(::Type{Prod{I1,I2}}) = tuple_type_cons(eltype(I1), eltype(I2)) -size(p::Prod) = (length(p.a), size(p.b)...) -ndims(p::Prod) = length(size(p)) 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)) @@ -400,7 +412,8 @@ iteratorsize{I1,I2}(::Type{Prod{I1,I2}}) = prod_iteratorsize(iteratorsize(I1),it end prod_iteratorsize(::Union{HasLength,HasShape}, ::Union{HasLength,HasShape}) = HasShape() -prod_iteratorsize(a, ::IsInfinite) = IsInfinite() # products can have an infinite last iterator (which moves slowest) +# products can have an infinite iterator +prod_iteratorsize(a, ::IsInfinite) = IsInfinite() prod_iteratorsize(::IsInfinite, b) = IsInfinite() prod_iteratorsize(a, b) = SizeUnknown() diff --git a/test/functional.jl b/test/functional.jl index 3fd94dc413a00..bea8ff68e6e1f 100644 --- a/test/functional.jl +++ b/test/functional.jl @@ -194,70 +194,123 @@ end @test collect(Base.product(1:2, 3:4, 5:6)) == [(i, j, k) for i=1:2, j=3:4, k=5:6] # iteration order -let expected = [(1,3,5), (2,3,5), (1,4,5), (2,4,5), (1,3,6), (2,3,6), (1,4,6), (2,4,6)] - i = 1 - for el in Base.product(1:2, 3:4, 5:6) - @test el == expected[i] - i+=1 +let + expected = [(1,3,5), (2,3,5), (1,4,5), (2,4,5), (1,3,6), (2,3,6), (1,4,6), (2,4,6)] + actual = Base.product(1:2, 3:4, 5:6) + for (exp, act) in zip(expected, actual) + @test exp == act end end -# is this the correct behaviour? -@test collect(Base.product([1 2; 3 4], [5 6; 7 8])) == [(1,5) (1,7) (1,6) (1,8); - (3,5) (3,7) (3,6) (3,8); - (2,5) (2,7) (2,6) (2,8); - (4,5) (4,7) (4,6) (4,8)] +# collect multidimensional array +let + a, b = 1:3, [4 6; + 5 7] + p = Base.product(a, b) + @test size(p) == (3, 2, 2) + @test length(p) == 12 + @test ndims(p) == 3 + @test eltype(p) == NTuple{2, Int} + cp = collect(p) + for i = 1:3 + @test cp[i, :, :] == [(i, 4) (i, 6); + (i, 5) (i, 7)] + end +end -let - a, b, c, d, e = 1:2, 1.0:10.0, 4f0:6f0, 0x01:0x08, Int8(1):Int8(0) +# with 1D inputs +let + a, b, c = 1:2, 1.0:10.0, Int32(1):Int32(0) # length - @test length(Base.product(a)) == 2 - @test length(Base.product(a, b)) == 20 - @test length(Base.product(a, b, c)) == 60 - @test length(Base.product(a, b, c, d)) == 480 - @test length(Base.product(a, b, c, d, e)) == 0 + @test length(Base.product(a)) == 2 + @test length(Base.product(a, b)) == 20 + @test length(Base.product(a, b, c)) == 0 # size - @test size(Base.product(a)) == (2, ) - @test size(Base.product(a, b)) == (2, 10) - @test size(Base.product(a, b, c)) == (2, 10, 3) - @test size(Base.product(a, b, c, d)) == (2, 10, 3, 8) - @test size(Base.product(a, b, c, d, e)) == (2, 10, 3, 8, 0) + @test size(Base.product(a)) == (2, ) + @test size(Base.product(a, b)) == (2, 10) + @test size(Base.product(a, b, c)) == (2, 10, 0) # eltype - @test eltype(Base.product(a)) == Tuple{Int} - @test eltype(Base.product(a, b)) == Tuple{Int, Float64} - @test eltype(Base.product(a, b, c)) == Tuple{Int, Float64, Float32} - @test eltype(Base.product(a, b, c, d)) == Tuple{Int, Float64, Float32, UInt8} - @test eltype(Base.product(a, b, c, d, e)) == Tuple{Int, Float64, Float32, UInt8, Int8} + @test eltype(Base.product(a)) == Tuple{Int} + @test eltype(Base.product(a, b)) == Tuple{Int, Float64} + @test eltype(Base.product(a, b, c)) == Tuple{Int, Float64, Int32} # ndims - @test ndims(Base.product(a)) == 1 - @test ndims(Base.product(a, b)) == 2 - @test ndims(Base.product(a, b, c)) == 3 - @test ndims(Base.product(a, b, c, d)) == 4 - @test ndims(Base.product(a, b, c, d, e)) == 5 - - f, g, h = randn(1, 1), randn(1, 1, 1), randn(1, 1, 1, 1) - @test ndims(Base.product(f)) == ndims(collect(Base.product(f))) == 2 - @test ndims(Base.product(g)) == ndims(collect(Base.product(g))) == 3 - @test ndims(Base.product(h)) == ndims(collect(Base.product(h))) == 4 - @test ndims(Base.product(f, f)) == ndims(collect(Base.product(f, f))) == 2 - @test ndims(Base.product(f, g)) == ndims(collect(Base.product(f, g))) == 2 - @test ndims(Base.product(g, g)) == ndims(collect(Base.product(g, g))) == 2 - @test ndims(Base.product(g, h)) == ndims(collect(Base.product(g, h))) == 2 - @test ndims(Base.product(h, h)) == ndims(collect(Base.product(h, h))) == 2 - @test ndims(Base.product(f, f, f)) == ndims(collect(Base.product(f, f, f))) == 3 - @test ndims(Base.product(f, f, g)) == ndims(collect(Base.product(f, f, g))) == 3 - @test ndims(Base.product(f, g, g)) == ndims(collect(Base.product(f, g, g))) == 3 - @test ndims(Base.product(g, g, g)) == ndims(collect(Base.product(g, g, g))) == 3 - @test ndims(Base.product(g, g, h)) == ndims(collect(Base.product(g, g, h))) == 3 - @test ndims(Base.product(g, h, h)) == ndims(collect(Base.product(g, h, h))) == 3 - @test ndims(Base.product(h, h, h)) == ndims(collect(Base.product(h, h, h))) == 3 - @test ndims(Base.product(f, f, f)) == ndims(collect(Base.product(f, f, f))) == 3 - @test ndims(Base.product(g, g, g, g)) == ndims(collect(Base.product(g, g, g, g))) == 4 - @test ndims(Base.product(h, h, h, h)) == ndims(collect(Base.product(h, h, h, h))) == 4 + @test ndims(Base.product(a)) == 1 + @test ndims(Base.product(a, b)) == 2 + @test ndims(Base.product(a, b, c)) == 3 +end + +# with multidimensional inputs +let + a, b, c = randn(4, 4), randn(3, 3, 3), randn(2, 2, 2, 2) + args = Any[(a,), + (a, a), + (a, b), + (a, a, a), + (a, b, c)] + sizes = Any[(4, 4), + (4, 4, 4, 4), + (4, 4, 3, 3, 3), + (4, 4, 4, 4, 4, 4), + (4, 4, 3, 3, 3, 2, 2, 2, 2)] + for (method, fun) in zip([size, ndims, length], [x->x, length, prod]) + for i in 1:length(args) + @test method(Base.product(args[i]...)) == method(collect(Base.product(args[i]...))) == fun(sizes[i]) + end + end +end + +# more tests on product with iterators of various type +let + iters = (1:2, + rand(2, 2, 2), + take(1:4, 2), + Base.product(1:2, 1:3), + Base.product(rand(2, 2), rand(1, 1, 1)) + ) + for method in [size, length, ndims, eltype] + for i = 1:length(iters) + args = iters[i] + @test method(Base.product(args...)) == method(collect(Base.product(args...))) + for j = 1:length(iters) + args = iters[i], iters[j] + @test method(Base.product(args...)) == method(collect(Base.product(args...))) + for k = 1:length(iters) + args = iters[i], iters[j], iters[k] + @test method(Base.product(args...)) == method(collect(Base.product(args...))) + end + end + end + end +end + +# product of finite length and infinite length iterators +let + a = 1:2 + b = countfrom(1) + ab = Base.product(a, b) + ba = Base.product(b, a) + abexp = [(1, 1), (2, 1), (1, 2), (2, 2), (1, 3), (2, 3)] + baexp = [(1, 1), (2, 1), (3, 1), (4, 1), (5, 1), (6, 1)] + for (expected, actual) in zip([abexp, baexp], [ab, ba]) + for (i, el) in enumerate(actual) + @test el == expected[i] + i == length(expected) && break + end + @test_throws ArgumentError length(actual) + @test_throws ArgumentError size(actual) + @test_throws ArgumentError ndims(actual) + end + + # size infinite or unknown raises an error + for itr in Any[countfrom(1), Filter(i->0, 1:10)] + @test_throws ArgumentError length(Base.product(itr)) + @test_throws ArgumentError size(Base.product(itr)) + @test_throws ArgumentError ndims(Base.product(itr)) + end end # iteratorsize trait business From e2ca6364e536e88bc54512ef20d12d42d9cea848 Mon Sep 17 00:00:00 2001 From: Davide Lasagna Date: Sat, 21 May 2016 17:04:09 +0100 Subject: [PATCH 3/4] Removed trailing whitespace --- base/iterator.jl | 8 ++++---- test/functional.jl | 26 +++++++++++++------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/base/iterator.jl b/base/iterator.jl index 627379b27485a..f9f783c80f815 100644 --- a/base/iterator.jl +++ b/base/iterator.jl @@ -310,15 +310,15 @@ ndims(p::AbstractProdIterator) = length(size(p)) # generic methods to handle size of Prod* types _prod_size(a, ::HasShape) = size(a) _prod_size(a, ::HasLength) = (length(a), ) -_prod_size(a, A) = +_prod_size(a, A) = throw(ArgumentError("Cannot compute size for object of type $(typeof(a))")) _prod_size(a, b, ::HasLength, ::HasLength) = (length(a), length(b)) _prod_size(a, b, ::HasLength, ::HasShape) = (length(a), size(b)...) _prod_size(a, b, ::HasShape, ::HasLength) = (size(a)..., length(b)) _prod_size(a, b, ::HasShape, ::HasShape) = (size(a)..., size(b)...) -_prod_size(a, b, A, ::Union{HasShape, HasLength}) = +_prod_size(a, b, A, ::Union{HasShape, HasLength}) = throw(ArgumentError("Cannot compute size for object of type $(typeof(a))")) -_prod_size(a, b, ::Union{HasShape, HasLength}, B) = +_prod_size(a, b, ::Union{HasShape, HasLength}, B) = throw(ArgumentError("Cannot compute size for object of type $(typeof(b))")) # one iterator @@ -413,7 +413,7 @@ end prod_iteratorsize(::Union{HasLength,HasShape}, ::Union{HasLength,HasShape}) = HasShape() # products can have an infinite iterator -prod_iteratorsize(a, ::IsInfinite) = IsInfinite() +prod_iteratorsize(a, ::IsInfinite) = IsInfinite() prod_iteratorsize(::IsInfinite, b) = IsInfinite() prod_iteratorsize(a, b) = SizeUnknown() diff --git a/test/functional.jl b/test/functional.jl index bea8ff68e6e1f..4d6f5109d3617 100644 --- a/test/functional.jl +++ b/test/functional.jl @@ -194,7 +194,7 @@ end @test collect(Base.product(1:2, 3:4, 5:6)) == [(i, j, k) for i=1:2, j=3:4, k=5:6] # iteration order -let +let expected = [(1,3,5), (2,3,5), (1,4,5), (2,4,5), (1,3,6), (2,3,6), (1,4,6), (2,4,6)] actual = Base.product(1:2, 3:4, 5:6) for (exp, act) in zip(expected, actual) @@ -203,10 +203,10 @@ let end # collect multidimensional array -let - a, b = 1:3, [4 6; +let + a, b = 1:3, [4 6; 5 7] - p = Base.product(a, b) + p = Base.product(a, b) @test size(p) == (3, 2, 2) @test length(p) == 12 @test ndims(p) == 3 @@ -219,7 +219,7 @@ let end # with 1D inputs -let +let a, b, c = 1:2, 1.0:10.0, Int32(1):Int32(0) # length @@ -246,10 +246,10 @@ end # with multidimensional inputs let a, b, c = randn(4, 4), randn(3, 3, 3), randn(2, 2, 2, 2) - args = Any[(a,), - (a, a), - (a, b), - (a, a, a), + args = Any[(a,), + (a, a), + (a, b), + (a, a, a), (a, b, c)] sizes = Any[(4, 4), (4, 4, 4, 4), @@ -266,9 +266,9 @@ end # more tests on product with iterators of various type let iters = (1:2, - rand(2, 2, 2), - take(1:4, 2), - Base.product(1:2, 1:3), + rand(2, 2, 2), + take(1:4, 2), + Base.product(1:2, 1:3), Base.product(rand(2, 2), rand(1, 1, 1)) ) for method in [size, length, ndims, eltype] @@ -288,7 +288,7 @@ let end # product of finite length and infinite length iterators -let +let a = 1:2 b = countfrom(1) ab = Base.product(a, b) From c6106f7ad08d5c11ebb06b678506f8fcedcaabda Mon Sep 17 00:00:00 2001 From: Davide Lasagna Date: Sat, 28 May 2016 13:19:12 +0100 Subject: [PATCH 4/4] Change iteratorsize trait of product removed old _size methods Fixed ambiguous methods --- base/iterator.jl | 11 ++++------- test/functional.jl | 1 + 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/base/iterator.jl b/base/iterator.jl index 9b52e90363b87..c3479f8fab74b 100644 --- a/base/iterator.jl +++ b/base/iterator.jl @@ -320,10 +320,8 @@ _prod_size(a, b, ::HasLength, ::HasLength) = (length(a), length(b)) _prod_size(a, b, ::HasLength, ::HasShape) = (length(a), size(b)...) _prod_size(a, b, ::HasShape, ::HasLength) = (size(a)..., length(b)) _prod_size(a, b, ::HasShape, ::HasShape) = (size(a)..., size(b)...) -_prod_size(a, b, A, ::Union{HasShape, HasLength}) = - throw(ArgumentError("Cannot compute size for object of type $(typeof(a))")) -_prod_size(a, b, ::Union{HasShape, HasLength}, B) = - throw(ArgumentError("Cannot compute size for object of type $(typeof(b))")) +_prod_size(a, b, A, B) = + throw(ArgumentError("Cannot construct size for objects of types $(typeof(a)) and $(typeof(b))")) # one iterator immutable Prod1{I} <: AbstractProdIterator @@ -417,12 +415,11 @@ end prod_iteratorsize(::Union{HasLength,HasShape}, ::Union{HasLength,HasShape}) = HasShape() # products can have an infinite iterator +prod_iteratorsize(::IsInfinite, ::IsInfinite) = IsInfinite() prod_iteratorsize(a, ::IsInfinite) = IsInfinite() prod_iteratorsize(::IsInfinite, b) = IsInfinite() prod_iteratorsize(a, b) = SizeUnknown() -_size(p::Prod2) = (length(p.a), length(p.b)) -_size(p::Prod) = (length(p.a), _size(p.b)...) """ IteratorND(iter, dims) @@ -443,7 +440,7 @@ immutable IteratorND{I,N} end new{I,N}(iter, shape) end - (::Type{IteratorND}){I<:AbstractProdIterator}(p::I) = IteratorND(p, _size(p)) + (::Type{IteratorND}){I<:AbstractProdIterator}(p::I) = IteratorND(p, size(p)) end start(i::IteratorND) = start(i.iter) diff --git a/test/functional.jl b/test/functional.jl index e53c2c395d8ed..754442d8bfbeb 100644 --- a/test/functional.jl +++ b/test/functional.jl @@ -334,6 +334,7 @@ let f1 = Filter(i->i>0, 1:10) @test Base.iteratorsize(Base.product(countfrom(1), f1)) == Base.IsInfinite() end @test Base.iteratorsize(Base.product(1:2, countfrom(1))) == Base.IsInfinite() +@test Base.iteratorsize(Base.product(countfrom(2), countfrom(1))) == Base.IsInfinite() @test Base.iteratorsize(Base.product(countfrom(1), 1:2)) == Base.IsInfinite() @test Base.iteratorsize(Base.product(1:2)) == Base.HasShape() @test Base.iteratorsize(Base.product(1:2, 1:2)) == Base.HasShape()