9

昨天我有机会想以相反的顺序迭代一个集合。我找到了这个reverse函数,但这并没有返回一个迭代器,而是实际上创建了一个反向集合。

显然,曾经有一个Reverse迭代器,它在几年前被删除了。我还可以找到对某些东西(一种类型?)的引用Order.Reverse,但它似乎不适用于我的问题。

Iterators.jl包有许多有趣的迭代模式,但显然没有反向迭代。

我当然可以使用该reverse函数,并且在某些情况下,例如reverse(eachindex(c))它返回一个反向迭代器,但我更喜欢通用反向迭代器。

有这样的事吗?

4

1 回答 1

7

更新 Julia 1.0+

您现在可以使用Iterators.reverse来反转有限的类型子集。例如:

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}})

请注意,这仅适用于已明确定义反向迭代的类型。也就是说,他们已经明确定义Base.iterate(::Iterators.Reverse{T}, ...)了哪里 T 是自定义类型。所以它不是完全通用的(出于下面记录的原因),但它确实适用于任何支持它的类型。


原始答案

Jeff 在三年前(在您链接的问题中)删除反向迭代器时的评论与今天同样重要:

我非常赞成删除它,因为它根本不起作用。与 iterator.jl 中的所有其他内容不同,它依赖于索引,而不是迭代,甚至不适用于可索引的所有内容(例如 UTF8String)。我讨厌在基地有这样的地雷。

在最基本的层面上,迭代器只知道如何做三件事:开始迭代,获取下一个元素,检查迭代是否完成。为了创建一个不使用这些原语进行分配的迭代器,您需要一个 O(n^2) 算法:遍历整个迭代器,边走边算,直到找到最后一个元素。然后再次遍历迭代器,只是这次停在倒数第二个元素处。当然它不会分配,但它会将迭代器收集到一个数组中然后向后索引要慢得多。对于一次性迭代器(如eachline),它将完全被破坏。所以根本不可能创建一个高效的通用反向迭代器。

请注意,这reverse(eachindex(c))通常不起作用:

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

仍然适用于偏移数组的一种替代方法是reverse(linearindices(c)).

于 2017-03-10T16:58:16.997 回答