Превращение нескольких перечислимых в один - PullRequest
0 голосов
/ 01 сентября 2018

Есть ли способ заставить несколько перечисляемых объектов отображаться как один перечисляемый без объединения его в массив? В настоящее время я написал такой класс, но я чувствую, что должно быть встроенное решение.

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

Ответы [ 3 ]

0 голосов
/ 05 сентября 2018

Я закончил с этим решением, может быть, близко к тому, что вы уже пробовали:

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
0 голосов
/ 06 сентября 2018

В конце концов. Используйте 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]
0 голосов
/ 01 сентября 2018

Ну, это можно сделать со стандартной библиотекой, используя Enumerator. Преимущество этого подхода состоит в том, что он возвращает перечислитель real , который может быть отображен, уменьшен и т. Д.

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]
...