diff --git a/base/generator.jl b/base/generator.jl index 1317aaf5223e0..9d94996be1d4f 100644 --- a/base/generator.jl +++ b/base/generator.jl @@ -52,6 +52,7 @@ size(g::Generator) = size(g.iter) axes(g::Generator) = axes(g.iter) ndims(g::Generator) = ndims(g.iter) keys(g::Generator) = keys(g.iter) +last(g::Generator) = g.f(last(g.iter)) ## iterator traits diff --git a/base/iterators.jl b/base/iterators.jl index a57d029b1ee16..8bde262ae9f65 100644 --- a/base/iterators.jl +++ b/base/iterators.jl @@ -160,6 +160,7 @@ size(e::Enumerate) = size(e.itr) n === nothing && return n (i, n[1]), (i+1, n[2]) end +last(e::Enumerate) = (length(e.itr), e.itr[end]) eltype(::Type{Enumerate{I}}) where {I} = Tuple{Int, eltype(I)} @@ -251,6 +252,10 @@ IteratorSize(::Type{<:Pairs{<:Any, <:Any, I}}) where {I} = IteratorSize(I) IteratorSize(::Type{<:Pairs{<:Any, <:Any, <:Base.AbstractUnitRange, <:Tuple}}) = HasLength() reverse(v::Pairs) = Pairs(getfield(v, :data), reverse(getfield(v, :itr))) +function last(v::Pairs{K, V}) where {K, V} + idx = last(getfield(v, :itr)) + return Pair{K, V}(idx, v[idx]) +end haskey(v::Pairs, key) = (key in getfield(v, :itr)) keys(v::Pairs) = getfield(v, :itr) @@ -398,7 +403,8 @@ zip_iteratoreltype() = HasEltype() zip_iteratoreltype(a) = a zip_iteratoreltype(a, tail...) = and_iteratoreltype(a, zip_iteratoreltype(tail...)) -reverse(z::Zip) = Zip(Base.map(reverse, z.is)) +reverse(z::Zip) = Zip(Base.map(reverse, z.is)) # n.b. we assume all iterators are the same length +last(z::Zip) = getindex.(z.is, minimum(Base.map(lastindex, z.is))) # filter @@ -457,6 +463,7 @@ IteratorEltype(::Type{Filter{F,I}}) where {F,I} = IteratorEltype(I) IteratorSize(::Type{<:Filter}) = SizeUnknown() reverse(f::Filter) = Filter(f.flt, reverse(f.itr)) +last(f::Filter) = first(reverse(f)) # Accumulate -- partial reductions of a function over an iterator @@ -883,6 +890,7 @@ function iterate(it::Cycle, state) end reverse(it::Cycle) = Cycle(reverse(it.xs)) +last(it::Cycle) = last(it.xs) # Repeated - repeat an object infinitely many times @@ -921,6 +929,7 @@ IteratorSize(::Type{<:Repeated}) = IsInfinite() IteratorEltype(::Type{<:Repeated}) = HasEltype() reverse(it::Union{Repeated,Take{<:Repeated}}) = it +last(it::Union{Repeated,Take{<:Repeated}}) = first(it) # Product -- cartesian product of iterators struct ProductIterator{T<:Tuple} @@ -1052,6 +1061,7 @@ end end reverse(p::ProductIterator) = ProductIterator(Base.map(reverse, p.iterators)) +last(p::ProductIterator) = Base.map(last, p.iterators) # flatten an iterator of iterators @@ -1131,6 +1141,7 @@ length(f::Flatten{Tuple{}}) = 0 end reverse(f::Flatten) = Flatten(reverse(itr) for itr in reverse(f.it)) +last(f::Flatten) = last(last(f.it)) """ partition(collection, n) diff --git a/test/iterators.jl b/test/iterators.jl index 388c76ab2d575..1b2498fb1f905 100644 --- a/test/iterators.jl +++ b/test/iterators.jl @@ -689,11 +689,18 @@ end Iterators.Filter(isodd, 1:10), flatten((1:10, 50:60)), enumerate("foo"), pairs(50:60), zip(1:10,21:30,51:60), product(1:3, 10:12), repeated(3.14159, 5), (a=2, b=3, c=5, d=7, e=11)) - @test squash(collect(Iterators.reverse(itr))) == reverse(squash(collect(itr))) + arr = reverse(squash(collect(itr))) + itr = Iterators.reverse(itr) + @test squash(collect(itr)) == arr + if !isempty(arr) + @test first(itr) == first(arr) + @test last(itr) == last(arr) + end end @test collect(take(Iterators.reverse(cycle(1:3)), 7)) == collect(take(cycle(3:-1:1), 7)) let r = repeated(3.14159) @test Iterators.reverse(r) === r + @test last(r) === 3.14159 end for t in [(1,), (2, 3, 5, 7, 11), (a=1,), (a=2, b=3, c=5, d=7, e=11)] @test Iterators.reverse(Iterators.reverse(t)) === t @@ -887,3 +894,8 @@ end @test Iterators.peel(x^2 for x in 2:4)[1] == 4 @test Iterators.peel(x^2 for x in 2:4)[2] |> collect == [9, 16] end + +@testset "last for iterators" begin + @test last(Iterators.map(identity, 1:3)) == 3 + @test last(Iterators.filter(iseven, (Iterators.map(identity, 1:3)))) == 2 +end