せかいや

いまいるここを、おもしろく http://sekai-in-the-box.appspot.com/

【Ruby】Enumeratorクラス その3。外部イテレータ、Fiber、yield

 
hp12cさんのブログの内容がすぐに理解できなかったので、手元で確認したよ。
http://melborne.github.io/2013/10/08/answer-to-is-this-a-bug-of-ruby-or-me/

外部イテレーターもFiberも理解したはずなのにぜんぜん分からない・・・!
ショック!
ちゃんと見直してみる。

 

内部クラスのインスタンス変数

本題とは関係ないけど、知らないとコードが追えなかったので。

インスタンス変数は、定義した最も内側のクラスだけが有効範囲となる。

class A
	class B
		def initialize
			@hoge=1
			p "B dayo #{@hoge}"
		end
	end
	class C
		def initialize
			B.new
			p "C dayo #{@hoge}"
		end
	end
end
A::C.new

■実行結果

"B dayo 1"
"C dayo "

 
 

Fiber.yieldの引数は@fiber.resumeの戻り値になる

Fiberの基本。

def reset
  @fiber = Fiber.new do
    @obj.each { |e| Fiber.yield(e*10) ;p "#{e} dayo"}
  end
end
def next
  @fiber.resume
end
p  odd.next
p  odd.next

■実行結果

10
"1 dayo"
30

Fiber.yieldによって制御が移るため、
2回目のpメソッドは実行されない。
 
 

resetメソッドの読み方

def reset
  @fiber = Fiber.new do
    @obj.each { |e| Fiber.yield(e*10) ;p "#{e} dayo"}
    raise StopIteration, "iteration has ended"
  end
end

resetメソッドではFiber.newしているのがポイント。
@obj.eachが実行されることによって、
変数currentの値がinitでリセットされる
 
nextメソッドは、eachをぐるぐる回しているわけではなく、
loopの中をぐるぐるしているだけ。
こんな風にpメソッドを入れれば理解できる。

class Generator
  def initialize(&blk1)
    @proc2 = blk1  #<= Enu.new do |y|の所のブロックが渡っている
  end
  def each(&blk2)  #<= { |e| Fiber.yield(e*10) ;p "#{e} dayo"} が渡っている
    p "gygy"
    @proc2[Yielder.new(&blk2)] # y = Yielder.new(&blk2)
  end
end
odd = step(1, 2)
p  odd.next 
p  odd.next 
  odd.rewind
p  odd.next 

initializeメソッドとeachメソッドの引数名が同じ事に惑わされたので、
変数名を&blk1と&blk2に変更してしまいました。
 
■実行結果

"gygy"
10
"1 dayo"
30
"3 dayo"
50
"gygy"
10

 

学んだこと

Fiberには手を出さない。