せかいや

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

【Ruby】Enumeratorクラス その2。外部イテレータ、内部イテレータ。

Enumratorクラスについては以前「初めてのRuby」で勉強したけど、
オライリー本にさらに詳しくいろいろ書いてある。


 

to_enumを使ってオブジェクト書き換えを防ぐ

配列が書き換えられることを心配するときは、
配列からto_enumを呼び出し、得られたEnumratorを配列自体の代わりに渡せばよい

ふむふむ。やってみよう。

 

p a = [1,2,3]
p a.to_enum.map{|x| x*x}
p a

 
■実行結果

[1, 2, 3]
[1, 4, 9]
[1, 2, 3]

なるほど。


 

ブロックが与えられていない時にEnumratorを返す、の実装方法

「初めてのRuby」7章3.3にはこうある。

Ruby1.9では、イテレーターをブロックなしで呼び出すと、
そのイテレーターをラップしたEnumeratorを生成して返します。

初めてのRubyはここまでだったけれど、
オライリー本には実装方法が載っている。
やってみよう。

class Car
  include Enumerable 
  def twice(x)
    if block_given?
      p "koko"
      yield x
      yield x
    else
      self.to_enum(:twice, x)
       #<= twiceという名前のメソッドを使って繰り返す
    end
  end
end

Car.new.twice(1){|x| p x}
p a = Car.new.twice(5)
p a.map{|x| x*x}
a.each{|x| p x}

 
■実行結果

"koko"
1
1
#<Enumerator: #<Car:0x2e0f260>:twice(5)>
"koko"
[25, 25]
"koko"
5
5

なるほど。
ブロック無しで呼び出すとEnumeratorが返る。

「5」「5」と5が二回出力されている。
Enumeratorオブジェクトのeachメソッドが二回実行されるのは、
eachが「生成時のパラメータに従ってブロックを繰り返す」から。
つまり、Enumeratorオブジェクト生成時に
「twiceという名前のメソッドを使って繰り返す」と実装したから。


いろいろ書いたけど、現実的には
この数独コード内のeach_unknown程度をよく使うのではないかなー。
ブロックを引数にとるメソッドは、
「どんな引数でそのブロックを実行させたいか」を実装するイメージ。

 

外部イテレーター、内部イテレータ

nextメソッドを使うことで、コレクションの要素を外部から取得できる。
外部イテレーターを使うメリットは、
並列イテレーションが実現できるところ。

これについては明日コードを書いて理解する。
おやすみなさい。。