10

Yesterday I had occasion to want to iterate over a collection in reverse order. I found the reverse function, but this does not return an iterator, but actually creates a reversed collection.

Apparently, there used to be a Reverse iterator, which was removed several years ago. I can also find reference to something (a type?) called Order.Reverse, but that does not seem to apply to my question.

The Iterators.jl package has many interesting iteration patterns, but apparently not reverse iteration.

I can of course use the reverse function, and in some cases, for example reverse(eachindex(c)) which returnes a reversed iterator, but I would prefer a general reverse iterator.

Is there such a thing?

DNF
  • 11,584
  • 1
  • 26
  • 40

1 Answers1

8

UPDATE FOR JULIA 1.0+

You can now use Iterators.reverse to reverse a limited subset of types. For example:

julia> Iterators.reverse(1:10)
10:-1:1

julia> Iterators.reverse(CartesianIndices(zeros(2,3))) |> collect
2×3 Array{CartesianIndex{2},2}:
 CartesianIndex(2, 3)  CartesianIndex(2, 2)  CartesianIndex(2, 1)
 CartesianIndex(1, 3)  CartesianIndex(1, 2)  CartesianIndex(1, 1)

julia> Iterators.Reverse([1,1,2,3,5]) |> collect
5-element Array{Int64,1}:
 5
 3
 2
 1
 1

# But not all iterables support it (and new iterables don't support it by default):
julia> Iterators.reverse(Set(1:2)) |> collect
ERROR: MethodError: no method matching iterate(::Base.Iterators.Reverse{Set{Int64}})

Note that this only works for types that have specifically defined reverse iteration. That is, they have specifically defined Base.iterate(::Iterators.Reverse{T}, ...) where T is the custom type. So it's not fully general purpose (for the reasons documented below), but it does work for any type that supports it.


ORIGINAL ANSWER

Jeff's comment when he removed the reverse iterator three years ago (in the issue you linked) is just as relevant today:

I am highly in favor of deleting this since it simply does not work. Unlike everything else in iterator.jl it depends on indexing, not iteration, and doesn't even work on everything that's indexable (for example UTF8String). I hate having landmines like this in Base.

At the most basic level, iterators only know how to do three things: start iteration, get the next element, and check if the iteration is done. In order to create an iterator that doesn't allocate using these primitives, you'd need an O(n^2) algorithm: walk through the whole iterator, counting as you go, until you find the last element. Then walk the iterator again, only this time stopping at the penultimate element. Sure it doesn't allocate, but it'll be way slower than just collecting the iterator into an array and then indexing backwards. And it'll be completely broken for one-shot iterators (like eachline). So it is simply not possible to create an efficient general reverse iterator.

Note that reverse(eachindex(c)) does not work in general:

julia> reverse(eachindex(sprand(5,5,.2)))
ERROR: MethodError: no method matching reverse(::CartesianRange{CartesianIndex{2}})

One alternative that will still work with offset arrays is reverse(linearindices(c)).

mbauman
  • 30,958
  • 4
  • 88
  • 123
  • I _did_ read that comment, but I didn't take it as meaning that reverse iteration was infeasible. And Stefan Karpinski's following comment didn't give off that impression either. Regardless, your arguments make a lot of sense, but would it be impractical to support reverse iteration for some subset of collections? I see that Python does have something like that: https://docs.python.org/3/library/functions.html#reversed – DNF Mar 10 '17 at 17:17
  • No, it wouldn't be impractical at all. It's just that nobody has defined such an interface yet — we just need someone to do that work and I have a feeling it'd be accepted. Ref. https://github.com/JuliaLang/julia/issues/4590#issuecomment-26684029 – mbauman Mar 10 '17 at 17:31
  • I see. Well, that answers my question. – DNF Mar 10 '17 at 17:32
  • [As of Julia 1.8](https://github.com/JuliaLang/julia/pull/42225), `eachline` actually supports reverse-order iteration for seekable files. – SGJ Mar 10 '23 at 20:25