#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 с волокнами (но, возможно, они слишком «высокоуровневые», если вы хотите понять все это, попробуйте с продолжениями):
Ответ №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. Раньше я немного спешил, но это делает его намного приятнее, спасибо!