将几个 Enumerables 变成一个
Turning several Enumerables into one
有没有办法让多个 Enumerable 对象显示为单个 Enumerable 而无需将其展平为数组?目前我已经写了一个这样的class,但我觉得必须有一个内置的解决方案。
class Enumerables
include Enumerable
def initialize
@enums = []
end
def <<(enum)
@enums << enum
end
def each(&block)
if block_given?
@enums.each { |enum|
puts "Enumerating #{enum}"
enum.each(&block)
}
else
to_enum(:each)
end
end
end
enums = Enumerables.new
enums << 1.upto(3)
enums << 5.upto(8)
enums.each { |s| puts s }
作为一个简单的例子,它需要能够像这样接受无限枚举数。
inf = Enumerator.new { |y| a = 1; loop { y << a; a +=1 } };
好吧,可以使用 Enumerator
通过标准库来完成。这种方法的优点是 returns 真实 枚举器,可以映射、减少等
MULTI_ENUM = lambda do |*input|
# dup is needed here to prevent
# a mutation of inputs when given
# as a splatted param
# (due to `input.shift` below)
input = input.dup.map(&:to_enum)
Enumerator.new do |yielder|
loop do
# check if the `next` is presented
# and mutate the input swiping out
# the first (already iterated) elem
input.first.peek rescue input.shift
# stop iteration if there is no input left
raise StopIteration if input.empty?
# extract the next element from
# the currently iterated enum and
# append it to our new Enumerator
yielder << input.first.next
end
end
end
MULTI_ENUM.(1..3, 4.upto(5), [6, 7]).
map { |e| e ** 2 }
#⇒ [1, 4, 9, 16, 25, 36, 49]
我最终得到了这个解决方案,可能与您已经尝试过的很接近:
def enumerate(*enum)
enum.each_with_object([]) { |e, arr| arr << e.to_a }.flatten
end
enumerate( 1..3, 5.upto(8), 3.times, 'a'..'c' ).each { |e| p e }
# => 1, 2, 3, 5, 6, 7, 8, 0, 1, 2, "a", "b", "c"
或(相同机制):
def enumerate(*enum)
enum.flat_map { |e| e.to_a }
end
毕竟。在元素上使用 Enumerable::Lazy#flat_map
和 .each.lazy
:
inf = Enumerator.new { |y| a = 1; loop { y << a; a += 1 } }
[(1..3).to_a, inf].lazy.flat_map { |e| e.each.lazy }.take(10).force
#⇒ [1, 2, 3, 1, 2, 3, 4, 5, 6, 7]
有没有办法让多个 Enumerable 对象显示为单个 Enumerable 而无需将其展平为数组?目前我已经写了一个这样的class,但我觉得必须有一个内置的解决方案。
class Enumerables
include Enumerable
def initialize
@enums = []
end
def <<(enum)
@enums << enum
end
def each(&block)
if block_given?
@enums.each { |enum|
puts "Enumerating #{enum}"
enum.each(&block)
}
else
to_enum(:each)
end
end
end
enums = Enumerables.new
enums << 1.upto(3)
enums << 5.upto(8)
enums.each { |s| puts s }
作为一个简单的例子,它需要能够像这样接受无限枚举数。
inf = Enumerator.new { |y| a = 1; loop { y << a; a +=1 } };
好吧,可以使用 Enumerator
通过标准库来完成。这种方法的优点是 returns 真实 枚举器,可以映射、减少等
MULTI_ENUM = lambda do |*input|
# dup is needed here to prevent
# a mutation of inputs when given
# as a splatted param
# (due to `input.shift` below)
input = input.dup.map(&:to_enum)
Enumerator.new do |yielder|
loop do
# check if the `next` is presented
# and mutate the input swiping out
# the first (already iterated) elem
input.first.peek rescue input.shift
# stop iteration if there is no input left
raise StopIteration if input.empty?
# extract the next element from
# the currently iterated enum and
# append it to our new Enumerator
yielder << input.first.next
end
end
end
MULTI_ENUM.(1..3, 4.upto(5), [6, 7]).
map { |e| e ** 2 }
#⇒ [1, 4, 9, 16, 25, 36, 49]
我最终得到了这个解决方案,可能与您已经尝试过的很接近:
def enumerate(*enum)
enum.each_with_object([]) { |e, arr| arr << e.to_a }.flatten
end
enumerate( 1..3, 5.upto(8), 3.times, 'a'..'c' ).each { |e| p e }
# => 1, 2, 3, 5, 6, 7, 8, 0, 1, 2, "a", "b", "c"
或(相同机制):
def enumerate(*enum)
enum.flat_map { |e| e.to_a }
end
毕竟。在元素上使用 Enumerable::Lazy#flat_map
和 .each.lazy
:
inf = Enumerator.new { |y| a = 1; loop { y << a; a += 1 } }
[(1..3).to_a, inf].lazy.flat_map { |e| e.each.lazy }.take(10).force
#⇒ [1, 2, 3, 1, 2, 3, 4, 5, 6, 7]