While responding to a different topic, I needed the last element of an iterator. This has at least one fairly simple and efficient solution which has appeared elsewhere:
itrlast(itr) = foldl((_,y)->y,itr)
but last
can also return several elements from the end of a collection, so here is a suggested function to achieve this generally:
using CircularArrays
using IterTools
function itrlast(itr, n::Integer)
n > 0 || error("Must keep at least one element")
pi = peekiter(itr)
e = peek(pi)
isnothing(e) && "Iterator is empty"
T = eltype(something(e))
buf, i = foldl(((buf,i),e)->(buf[i+1] = e; (buf,i+1)),pi;
init=(CircularArray(Vector{T}(undef,n)),0))
i<n && error("Iterator has less than $n elements")
@view buf[i-n+1:i]
end
With this method defined, the following works:
julia> itrlast(1:10,2)
2-element view(CircularVector(::Vector{Int64}), 9:10) with eltype Int64:
9
10
and also:
julia> itrlast(Iterators.take(iterated(x->√(6+x),1.0),23),5)
5-element view(CircularVector(::Vector{Float64}), 19:23) with eltype Float64:
2.9999999999999787
2.9999999999999964
2.9999999999999996
3.0
3.0
This function, and perhaps last
as well, can be generalized to “index” any iterator with an index range (e.g. 1:3:10 or ?? last(1:3:end, 5) of an itr ??).
Comments, ideas welcome.