Когда полезен метод Enumerator :: Yielder # yield? - PullRequest
14 голосов
/ 22 февраля 2011

В этом вопросе упоминается метод Enumerator::Yielder#yield. Я не использовал его раньше, и мне интересно, при каких обстоятельствах это будет полезно.

Полезно ли это в основном, когда вы хотите создать бесконечный список предметов, таких как Сито Эратосфена, и когда вам нужно использовать внешний итератор?

Ответы [ 4 ]

11 голосов
/ 22 февраля 2011

Хотя это довольно удобно для создания бесконечных списков и ленивых итераторов, мое любимое использование - обернуть существующий Enumerable дополнительными функциями (любым перечислимым, без необходимости знать, что это на самом деле, является ли он бесконечным или нет).и т.д.).

Тривиальным примером будет реализация метода each_with_index (или, в более общем смысле, метода with_index):

module Enumerable
  def my_with_index
    Enumerator.new do |yielder|
      i = 0
      self.each do |e|
        yielder.yield e, i
        i += 1
      end
    end
  end

  def my_each_with_index
    self.my_with_index.each do |e, i|
      yield e, i
    end
  end
end

[:foo, :bar, :baz].my_each_with_index do |e,i|
  puts "#{i}: #{e}"
end
#=>0: foo
#=>1: bar
#=>2: baz

Теперь давайте расширим то, что еще не реализовано в corelib:), например, циклическое присваивание значения из данного массива каждому перечисляемому элементу (скажем, для строк таблицы раскраски):

module Enumerable
  def with_cycle values
    Enumerator.new do |yielder|
      self.each do |e|
        v = values.shift
        yielder.yield e, v
        values.push v
      end
    end
  end
end

p (1..10).with_cycle([:red, :green, :blue]).to_a # works with any Enumerable, such as Range
#=>[[1, :red], [2, :green], [3, :blue], [4, :red], [5, :green], [6, :blue], [7, :red], [8, :green], [9, :blue], [10, :red]]

Весь смысл в том, что эти методы возвращают Enumerator, который затем объединяется сОбычные перечисляемые методы, такие как select, map, inject и т. д.

1 голос
/ 31 января 2018

Например, вы можете использовать его для построения встроенных тел ответа Rack, не создавая классов.Enumerator также может работать "снаружи-внутрь" - вы вызываете Enumerator#each, который вызывает next на счетчике и возвращает каждое значение в последовательности.Например, вы можете сделать тело ответа стойки, возвращающее последовательность чисел:

run ->(env) {
  body = Enumerator.new do |y|
   9.times { |i| y.yield(i.to_s) }
  end
  [200, {'Content-Length' => '9'}, body]
}
1 голос
/ 26 февраля 2011

Поскольку Младен упоминал о получении других ответов, я подумал, что приведу пример того, что я только что сделал сегодня сегодня. Я пишу приложение, которое будет получать данные с нескольких физических устройств, анализировать данные и связывать связанные данные (которые мы видим с нескольких устройств). Это долго работающее приложение, и если бы я никогда не выбрасывал данные (скажем, по крайней мере за сутки без обновлений), он бы рос бесконечно большим.

Я изучаю Ruby только 7-8 месяцев, поэтому в прошлом я бы делал что-то вроде этого:

delete_old_stuff if rand(300) == 0

и выполните это, используя случайные числа. Однако это не является чисто детерминированным. Я знаю, что он будет выполняться примерно один раз каждые 300 оценок (т. Е. В секундах), но не будет точно один раз каждые 300 раз.

То, что я написал ранее, выглядит так:

counter = Enumerator.new do |y|
  a = (0..300)
  loop do
    a.each do |b|
      y.yield b
    end
    delete_old_stuff
  end
end

и я могу заменить delete_old_stuff if rand(300) == 0 на counter.next

Теперь, я уверен, что есть 1) более эффективный или 2) заранее сделанный способ сделать это, но меня заинтересовал вопрос с Enumerator::Yielder#yield вашим вопросом и связанным вопросом, вот к чему я пришел с.

(и, очевидно, если вы видите способ улучшить это или что-то еще, дайте мне знать! Я хочу узнать как можно больше)

0 голосов
/ 28 марта 2012

Это кажется полезным, когда у вас есть несколько объектов, для которых вы хотите перечислить, но flat_map не подходит, и вы хотите связать перечисление с другим действием:

module Enumerable
  def count_by
    items_grouped_by_criteria = group_by {|object| yield object}
    counts = items_grouped_by_criteria.map{|key, array| [key, array.length]}
    Hash[counts]
  end
end

def calculate_letter_frequencies
  each_letter.count_by {|letter| letter}
end

def each_letter
  filenames = ["doc/Quickstart", "doc/Coding style"]
  # Joining the text of each file into a single string would be memory-intensive
  enumerator = Enumerator.new do |yielder|
    filenames.each do |filename|
      text = File.read(filename)
      text.chars.each {|letter| yielder.yield(letter)}
    end
  end
  enumerator
end

calculate_letter_frequencies
...