From 1f484c3ecabdd7519fa68569b1688a153e92fb16 Mon Sep 17 00:00:00 2001 From: Mason Protter Date: Wed, 10 Nov 2021 13:56:53 -0700 Subject: [PATCH] make `last` work on any reversible collection (#42991) --- NEWS.md | 2 ++ base/abstractarray.jl | 11 ++++++++--- base/iterators.jl | 1 - test/iterators.jl | 5 +++++ 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/NEWS.md b/NEWS.md index ce9a0d0551d47..3eaf21ff8f8fb 100644 --- a/NEWS.md +++ b/NEWS.md @@ -25,6 +25,8 @@ Language changes * `@time` and `@timev` now take an optional description to allow annotating the source of time reports. i.e. `@time "Evaluating foo" foo()` ([#42431]) * New `@showtime` macro to show both the line being evaluated and the `@time` report ([#42431]) +* `last(collection)` will now work on any collection that supports `Iterators.reverse` and `first`, rather than being + restricted to indexable collections. Compiler/Runtime improvements ----------------------------- diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 2733b52222e37..ac8b8e983c168 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -462,11 +462,15 @@ end last(coll) Get the last element of an ordered collection, if it can be computed in O(1) time. This is -accomplished by calling [`lastindex`](@ref) to get the last index. Return the end -point of an [`AbstractRange`](@ref) even if it is empty. +accomplished by calling [`Iterators.reverse`](@ref) and then [`first`](@ref) on that reversed iterator. +Return the end point of an [`AbstractRange`](@ref) even if it is empty. See also [`first`](@ref), [`endswith`](@ref). +!!! compat "Julia 1.8" + For versions of julia older than 1.8, `last(x)` will only work on collections that support indexing and + [`lastindex`](@ref). + # Examples ```jldoctest julia> last(1:2:10) @@ -476,7 +480,8 @@ julia> last([1; 2; 3; 4]) 4 ``` """ -last(a) = a[end] +last(a) = first(Iterators.reverse(a)) +last(a::AbstractVector) = a[end] """ last(itr, n::Integer) diff --git a/base/iterators.jl b/base/iterators.jl index 48f9017fddce5..cc3adad3816e4 100644 --- a/base/iterators.jl +++ b/base/iterators.jl @@ -103,7 +103,6 @@ size(r::Reverse) = size(r.itr) IteratorSize(::Type{Reverse{T}}) where {T} = IteratorSize(T) IteratorEltype(::Type{Reverse{T}}) where {T} = IteratorEltype(T) last(r::Reverse) = first(r.itr) # the first shall be last -first(r::Reverse) = last(r.itr) # and the last shall be first # reverse-order array iterators: assumes more-specialized Reverse for eachindex @propagate_inbounds function iterate(A::Reverse{<:AbstractArray}, state=(reverse(eachindex(A.itr)),)) diff --git a/test/iterators.jl b/test/iterators.jl index 8de847018255d..5002e7ec473e6 100644 --- a/test/iterators.jl +++ b/test/iterators.jl @@ -885,3 +885,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