1. 要素を取り出す each メソッド
Ruby の「イテレータ」は、複数の要素を持つオブジェクトが、個々の要素を取り出し、何らかの処理を適用するときに使うと便利。
一番基本となるメソッドが each 。例えば、Array クラスの場合、each メソッドで、要素をひとつづづ取り出すことができる。
[1,2,3,4,5].each do |i| puts i end
これはどういう仕組みで、このように書けるのだろうか?
2. イテレータの役割
「オブジェクト指向スクリプト言語 Ruby」(p94) では、イテレータについて、次のように述べられている。イテレータ (Iterator) はメソッドの一種で、もともとは繰り返しの抽象化のためのものでした。
しかし、はじめから 「繰り返しの抽象化のため」 という視点で、イテレータを理解しようとすると、わかりずらい。「繰り返しの処理のため」にあるというよりは、
関数 (処理) をメソッドに渡すための仕組み
と理解しておく方がよい。
3. yield の意味
イテレータに関して、最初に覚えておく単語は、
yield
これは、渡された関数に処理を移すための命令。もっと端的言うなら、yield が書かれたメソッドに渡されたブロックを、実行するための手段。Ruby では、関数と言わずに 「ブロック」 と呼ぶので、以降ブロックと呼ぶ。
ちなみに、Yahoo!辞書 - yield を調べると、その中の 1 つの意味として、
3 [III[名]([副])] …を放棄する, 手放す, (…に)明け渡す((up/to ...)); …
((~ -self))(誘惑などに)身を任せる, ふける((to ...));
[V[名](as)[名]] 身を任せて(…に)なる...
They yielded (up) the city [=the city (up)] to the enemy.
町を敵の手にゆだねた
Ruby の yield に当てはめて意味を考えると、
渡されたブロックに処理を「明け渡す、身を任せる」
と言った意味として捉えておけばいいかな。
4. 引数の数を変えてyield を呼び出してみる
引数なしで yield を呼び出す
渡されたブロックを処理する関数 test を定義してみる。
def test yield end test{puts "hoge"}
test を呼出すと、呼出したときに渡したブロックが yield によって呼出される。
yield に引数を1つ渡す
yield に引数を与えると、yield が書かれたメソッドに渡されたブロックの、ブロック変数に引数が渡される。
ブロック変数とは、ブロックの先頭で
|変数|
と記述されている変数のこと。
def test2 yield "hoge" end test2{|x| puts x}
yield に複数の引数を渡す
yield に引数を複数渡すと、それに対応したブロック変数に渡される。
def test3 yield "hoge", 100 end test3{|x, y| print x, y, "\n"}
5. block_given? メソッドで、ブロックのある・なしに対応
これまでは、メソッドに必ずブロックがある場合を試した。これを、ブロックが渡される場合と、渡されない場合に対応できるメソッドに変更する。
def test4 if block_given? yield else puts "no blcock" end end test4{puts "hoge"} test4
6. 要素を取り出す、イテレータとして用いる yield
yield を複数回呼び出す
yield は、渡されたブロックを実行するための手段であることが実感できた。
次は、「要素を走査するイテレータ」の使い方を、yield を用いて書く。
def test5 for i in [1,2,3] yield end end test5{puts "hoge"}
for ループにより、yield が 3 回実行されている。これにより、渡したブロックが 3 回される。
yield を複数回呼び出すとき、引数を一つ渡す
上記を少しだけ変更する。 yield を呼出す際、for ループで使われている変数 i の値を渡す。
def test6 for i in [1,2,3] yield i end end test6{|x| puts x}
これにより、先ほどと同じく、渡されたブロックが 3 回実行されるが、その度にブロック変数に渡される値が異なる。
yield を複数回呼び出すとき、引数を複数渡す
yield を複数回呼び出す。今度は値を複数渡してみる。
def test7 for i in [1,2,3] yield i, "hoge" end end test7{|x,y| print x, y, "\n"}
7. 「引数」と「ブロック」を受け取るメソッドの定義
引数を受け取るメソッドを定義する。ただし、ブロックも受け取る。
以下のメソッドでは、引数の値に応じて、ブロックの呼出しを変化させている。
def test8(val) for i in [1,2,3,4,5] yield i, "hoge" if i > val end end test8(0){|x,y| print x, y, "\n"} test8(2){|x,y| print x, y, "\n"}
このように、Ruby では、直接関数を渡すのではなく、「ブロック」を利用するところに特徴がある。慣れないと、素直に関数を渡せた方がシンプルでいいのになと感じる。 ^^;
「Ruby のイテレータ (2) – Enumerable と Comparable モジュール」へつづく…
1コメント:
イテレーターが使い方がよくわからなかったのですが、具体例をコマンドプロンプトで試したら理解できました。
ありがとうございます。
コメントを投稿