Как мне реализовать базовый класс Enumerator?

#ruby #ienumerator

#ruby #ienumerator

Вопрос:

Я пытаюсь понять, как работает класс Enumerator. В частности, я не знаю, как создается объект yielder и передается в блок кода, который принимает конструктор.

Вот моя первая попытка:

 class MyEnumerator
  def initialize(amp;block)
    @block = block
  end 
  def next()
    @block.call self
  end 
  def yield(*args)
    args
  end 
end


num_gen = MyEnumerator.new do |yielder|
  (1..10).each { |num| yielder.yield num }
end

5.times { p num_gen.next }
  

Конечно, это не работает, потому что я не знаю, как продвинуть enumerator. Может ли кто-нибудь помочь мне понять, как я могу это реализовать?

Ответ №1:

Вы должны использовать какой-то механизм продолжения. Проверьте:

http://www.ruby-doc.org/docs/ProgrammingRuby/html/ref_c_continuation.html

http://ruby-doc.org/docs/ProgrammingRuby/html/ref_m_kernel.html#Kernel.callcc

Кроме того, должно быть довольно тривиально реализовать enumerators с волокнами (но, возможно, они слишком «высокоуровневые», если вы хотите понять все это, попробуйте с продолжениями):

http://www.ruby-doc.org/core-1.9.2/Fiber.html

Ответ №2:

Вот один из способов создания базового enumerator (обновленный с учетом предложения Токланда):

 class MyEnumerator
  def initialize
    @fiber = Fiber.new { yield Fiber }
  end

  def next
    @fiber.resume
  end
end
  

Использование:

 >> num_gen = MyEnumerator.new { |f| (1..10).each { |x| f.yield x } }
=> #<MyEnumerator:0x007fd6ab8f4b28 @fiber=#<Fiber:0x007fd6ab8f4ab0>>
>> num_gen.next
=> 1
>> num_gen.next
=> 2
>> num_gen.next
=> 3
>> num_gen.next
=> 4
  

Комментарии:

1. Аккуратно. Может быть, «@fiber = Fiber.new { yield Fiber }» для эмуляции поведения Enumerator?

2. Раньше я немного спешил, но это делает его намного приятнее, спасибо!