Julia 中的反向迭代
Reverse iteration in Julia
昨天我有机会想要以相反的顺序迭代一个集合。我找到了 reverse
函数,但这不是 return 迭代器,而是实际上创建了一个反向集合。
显然,曾经有一个 Reverse
迭代器,它是 removed several years ago. I can also find reference 某种叫做 Order.Reverse
的东西(一种类型?),但这似乎不适用于我的问题.
Iterators.jl
包有很多有趣的迭代模式,但显然不是反向迭代。
我当然可以使用 reverse
函数,在某些情况下,例如 reverse(eachindex(c))
其中 return 是一个反向迭代器,但我更喜欢通用的反向迭代器。
有这种东西吗?
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 在三年前(在 the issue you linked 中)删除反向迭代器时的评论在今天同样适用:
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.
在最基本的层面上,迭代器只知道如何做三件事:开始迭代、获取下一个元素以及检查迭代是否完成。为了创建一个不使用这些原语进行分配的迭代器,您需要一个 O(n^2) 算法:遍历整个迭代器,边走边数,直到找到最后一个元素。然后再次 遍历迭代器,只是这次在倒数第二个元素处停止。当然它不分配,但它会 way 比仅将迭代器收集到数组中然后向后索引要慢。对于一次性迭代器(如 eachline
),它将完全被破坏。所以根本不可能创建一个高效的通用反向迭代器。
请注意,reverse(eachindex(c))
通常不起作用:
julia> reverse(eachindex(sprand(5,5,.2)))
ERROR: MethodError: no method matching reverse(::CartesianRange{CartesianIndex{2}})
仍然适用于偏移数组的一种替代方法是 reverse(linearindices(c))
。
昨天我有机会想要以相反的顺序迭代一个集合。我找到了 reverse
函数,但这不是 return 迭代器,而是实际上创建了一个反向集合。
显然,曾经有一个 Reverse
迭代器,它是 removed several years ago. I can also find reference 某种叫做 Order.Reverse
的东西(一种类型?),但这似乎不适用于我的问题.
Iterators.jl
包有很多有趣的迭代模式,但显然不是反向迭代。
我当然可以使用 reverse
函数,在某些情况下,例如 reverse(eachindex(c))
其中 return 是一个反向迭代器,但我更喜欢通用的反向迭代器。
有这种东西吗?
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 在三年前(在 the issue you linked 中)删除反向迭代器时的评论在今天同样适用:
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.
在最基本的层面上,迭代器只知道如何做三件事:开始迭代、获取下一个元素以及检查迭代是否完成。为了创建一个不使用这些原语进行分配的迭代器,您需要一个 O(n^2) 算法:遍历整个迭代器,边走边数,直到找到最后一个元素。然后再次 遍历迭代器,只是这次在倒数第二个元素处停止。当然它不分配,但它会 way 比仅将迭代器收集到数组中然后向后索引要慢。对于一次性迭代器(如 eachline
),它将完全被破坏。所以根本不可能创建一个高效的通用反向迭代器。
请注意,reverse(eachindex(c))
通常不起作用:
julia> reverse(eachindex(sprand(5,5,.2)))
ERROR: MethodError: no method matching reverse(::CartesianRange{CartesianIndex{2}})
仍然适用于偏移数组的一种替代方法是 reverse(linearindices(c))
。