【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程度をよく使うのではないかなー。
ブロックを引数にとるメソッドは、
「どんな引数でそのブロックを実行させたいか」を実装するイメージ。