Ruby Enumerator-based lazy flatten method
Michael Harrison在Ruby中的懒惰枚举器上有一篇不错的文章,提供了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | class Enumerator def lazy_flatten Enumerator.new do |yielder| self.each do |value| if value.kind_of? Enumerator value.lazy_flatten.each do |v| yielder.yield v end elsif value.kind_of? Enumerable value.flatten.each do |v| yielder.yield v end else yielder.yield value end end end end end |
这是我的实现方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | module Enumerable def lazy_flatten Enumerator.new do |yielder| each do |element| if element.is_a? Enumerable element.lazy_flatten.each do |e| yielder.yield(e) end else yielder.yield(element) end end end end end |
请注意,在Ruby 2.0中,您无需执行此操作,只需使用
由于我不清楚的原因, A value x returned by block is decomposed if either of the following conditions is true:
2
3
4
5
def lazy_flatten
self.lazy.flat_map { |x| x }
end
end
请注意,
1 2 3 4 5 6 | a = [[1, 2, 3], Set[4, 5], 6, 7..8] # => [[1, 2, 3], #<Set: {4, 5}>, 6, 7..8] f = a.lazy_flatten # => #<Enumerator::Lazy: #<Enumerator::Lazy: [[1, 2, 3], #<Set: {4, 5}>, 6, 7..8]>:flat_map> f.to_a # => [1, 2, 3, #<Set: {4, 5}>, 6, 7..8] |
但是,这与
的行为相同
1 2 | a.flatten # => [1, 2, 3, #<Set: {4, 5}>, 6, 7..8] |
(尽管
鉴于OP的代码或Mladen Jablanovi中的代码?\\的答案将分解
1 2 3 4 | f = a.lazy_flatten # (M.J.'s code) # => #<Enumerator: #<Enumerator::Generator:0x00007fd819c166c0>:each> f.to_a # => [1, 2, 3, 4, 5, 6, 7, 8] |
但是,如果传递的代码包含无限序列,该代码也将无限迭代:
1 2 3 4 5 6 | a = [[1, 2, 3], Set[4, 5], 6, 7..8, 9..Float::INFINITY] # => [[1, 2, 3], #<Set: {4, 5}>, 6, 7..8, 9..Infinity] f = a.lazy_flatten # (M.J.'s code) # => #<Enumerator: #<Enumerator::Generator:0x00007fd819a73d18>:each> f.to_a # => spins at 100% CPU for a while and eventually runs out of memory |
如果您认为是功能而不是错误,则一种方法是修改基于
1 2 3 4 5 6 7 | module Enumerable def lazy_flatten self.lazy.flat_map do |x| x.respond_to?(:lazy) ? x.lazy : x end end end |
这对于嵌套的惰性枚举也有效,因为