Using the example from the Julia Docs, we can define an iterator like the following:
struct Squares
count::Int
end
Base.iterate(S::Squares, state=1) = state > S.count ? nothing : (state*state, state+1)
Base.eltype(::Type{Squares}) = Int # Note that this is defined for the type
Base.length(S::Squares) = S.count
But even though there's a length
defined, asking for last(Squares(5))
results in an error:
julia> last(Squares(5))
ERROR: MethodError: no method matching lastindex(::Squares)
Since length
is defined, is there a way to iterate through and return the last value without doing an allocating collect
? If so, would it be bad to extend the Base.last
method for my type?
As you can read in the docstring of last
:
Get the last element of an ordered collection, if it can be computed in O(1) time. This is accomplished by calling
lastindex
to get the last index.
The crucial part is O(1) computation time. In your example the cost of computing last element is O(count
) (of course if we want to use the definition of the iterator as in general it would be possible compute it in O(1) time).
The idea is to avoid defining last
for collections for which it is expensive to compute it. For this reason the default definition of last is:
last(a) = a[end]
which requires not only lastindex
but also getindex
defined for the passed value (as the assumption is that if someone defines lastindex
and getindex
for some type then these operations can be performed fast).
If you look at Interfaces section of the Julia manual you will notice that the iteration interface (something that your example implements) is less demanding than indexing interface (something that is defined for your example in the next section of the manual). Usually the distinction is made that indexing interface is only added for collections that can be indexed efficiently.
If you still want last
to work on your type you can either:
Base.last
specifically - there is nothing wrong with doing this;getindex
, firstindex
, and lastindex
to make the collection indexable (and then the default definition of last
would work) - this is the approach presented in the Julia manual