Ruby TIPS
ブロック ― ちょっと便利な繰り返し処理の構文とは?(3)
Rubyに用意されている繰り返し処理の構文のうち「ブロック」を使えば、繰り返し処理をより簡潔に書ける。その基本的な使い方と、自作メソッドでの利用例を解説する。
Rubyの繰り返し処理には、ブロックと呼ばれる機能が利用できる。ブロックを利用すれば、while
文やuntil
文、for
文などを使った繰り返し処理よりも簡潔な書き方で、オブジェクトの各要素に対する処理ができる。今回は、ブロックを使った繰り返し処理の書き方と、自作のメソッドでブロックを利用できるようにする方法を見ていく。
ブロックを利用する
ブロックとは、いわゆるイテレーター(反復子)を実装したものである。イテレーターという用語になじみのない人には、実感がわきにくいかもしれないが、要するに、オブジェクトの要素や値を1つずつ取り出して処理することと考えればよい。前回の繰り返し処理についての解説(「TIPS: for文 ― ちょっと便利な繰り返し処理の構文とは?(2)」)でも簡単な例を示したが、まずは使い方から丁寧に見ていこう。
ブロックの使い方(1) - Integerクラスのtimesメソッド
例えば、整数(Integer
)クラスにはtimes
というメソッドがあり、整数の値の回数だけ繰り返し処理が実行できる。最初は、ブロック(正確には後述するブロックパラメーター)を使わずに単純に繰り返し処理を行う例を見てみる。
3.times do
puts "Hello Ruby!"
end
|
3
というリテラルは整数クラスのオブジェクト(正確にはFixnum
クラスのオブジェクト)として扱われるので、times
メソッドの後のdo
~end
の間に書かれている文が3回実行される。
なお、do
~end
の代わりに{
~}
を使ってもよい。実行結果は容易に想像できると思うが、念のため掲載しておこう。
$ ruby sample001.rb
Hello Ruby!
Hello Ruby!
Hello Ruby!
$
|
プログラムを実行すると、times
メソッドの働きによりHello Ruby!
が3回表示される。
この段階では、times
メソッドによる繰り返し処理を行っただけで、ブロックの機能を全て使っているわけではないことに注意してほしい。なお、Integer
オブジェクトの値が0以下の場合、times
メソッドによる繰り返しは実行されない。
ブロックパラメーターの使い方
続けて見ていこう。繰り返し処理の中では、何回目の繰り返しであるかを知りたいことが多い。そこで、ブロックパラメーターを利用する。|
と|
で囲まれた範囲にブロックパラメーターと呼ばれる変数を書いておけば、その変数に毎回、値が渡される。times
メソッドの場合は、0~整数の値-1が渡される。ブロックパラメーターの変数名は自由に付けてよい。以下の例では、i
という名前にした。
3.times do |i|
puts "Hello Ruby!" + i.to_s
end
|
ブロックパラメーターの名前はi
とした。times
メソッドの場合、繰り返しを実行するたびに0、1、2……という値が順にブロックパラメーターに渡される。+
演算子を使って文字列を連結するために、変数i
の値をto_s
メソッドで文字列に変換していることにも注意。
times
メソッドのように、ブロックパラメーターが指定できるメソッドのことをブロック付きメソッドと呼ぶ。
実行結果は以下の通り。ブロックパラメーターを利用することによって、繰り返しが何回目であるかということが分かるようになった。
$ ruby sample002.rb
Hello Ruby!0
Hello Ruby!1
Hello Ruby!2
$
|
プログラムを実行すると、times
メソッドの働きにより処理が3回繰り返される。繰り返しを実行するごとに、変数i
には0、1、2が順に渡される。
[まとめ]ブロックの使い方
書き方を十分理解した上で次に進みたいので、ここで、ブロックの使い方を図解しておく。
ここでは、Integer
クラスのtime
メソッドを例に、ブロック付きのメソッドの使い方とブロックパラメーターの指定方法を見てきたが、あくまでもtime
メソッドの使い方なので、他のメソッドの場合は動作が異なることに注意してほしい。
ブロックの使い方(2) - Integerクラスのupto/downtoメソッド
例えば、Integer
クラスのupto
メソッドでは、整数オブジェクトの値から引数の値までを順に返す。
5.upto(8) do |i|
puts "Hello Ruby!" + i.to_s
end
|
5
という整数オブジェクトのupto
メソッドを呼び出す。引数に8を指定すると、5~8までの(つまり4回の)繰り返し処理が行われる。繰り返し処理が実行されるたびに、5、6、7、8が順にブロックパラメーターに渡される。
実行例は以下の通り。ちなみに、upto
メソッドとは逆に、値を減らしていくdownto
メソッドもある。引数やブロックパラメーターの指定方法は同じである。
$ ruby sample003.rb
Hello Ruby!5
Hello Ruby!6
Hello Ruby!7
Hello Ruby!8
$
|
upto
メソッドでは、引数(8)までの値が順にブロックパラメーターに渡される。開始値は整数オブジェクトの値(5)そのものなので、5~8の値が順にブロックパラメーターに渡されることになる。
ブロックの使い方(3) - Arrayクラスのeachメソッド
もう1つ例を見ておこう。配列を表すArray
クラスではeach
メソッドが使える。each
メソッドは、配列の要素数だけ繰り返し処理を実行する。ブロックパラメーターには配列の各要素が順に渡される。
a = [2, 3, 5, 7, 11]
a.each do |i|
puts "Hello Ruby!" + i.to_s
end
|
配列a
の要素数は5なので、繰り返し処理は5回実行される。ブロックパラメーターのi
には配列a
の要素が順に渡される。
実行例は以下の通り。
$ ruby sample004.rb
Hello Ruby!2
Hello Ruby!3
Hello Ruby!5
Hello Ruby!7
Hello Ruby!11
$
|
ブロックパラメーターには配列の要素(5つの素数)が順に渡されるので、繰り返し処理によりそれらが順に表示される。
ブロック付きメソッドを作成する
これまで、ブロックパラメーターを指定して、ブロック付きメソッドを呼び出す方法を見てきた。ここからは、ブロック付きメソッドを自分で作成する方法を見ていく。やはり簡単な例から見ていこう。yield
メソッドを使えば、ブロックパラメーターに渡す値を指定できる。
引数なしのブロック付きメソッドの作り方 ― yieldメソッドの使い方
以下の例は、"red"、"green"、"blue"という文字列を順に返すだけのcolorEnum
メソッドの作成例と利用例である。colorEnum
メソッドの中では、yield
メソッドの引数にブロックパラメーターに渡す値を指定する。繰り返しの回数はyield
メソッドが呼び出される回数と考えればよい。
def colorEnum # colorEnumメソッドを定義する
yield "red"
yield "green"
yield "blue"
end
colorEnum do |x| # colorEnumメソッドを呼び出す
puts x
end
|
ここで作成したcolorEnum
メソッドではyield
メソッドが3回実行されるので、繰り返し数は3となり、puts
メソッドは3回実行される。ブロックパラメーターにはyield
メソッドの引数に指定した"red"、"green"、"blue"が順に渡される。
このプログラムの実行結果も容易に想像できるが、以下に示しておこう。
$ ruby sample005.rb
red
green
blue
$
|
yield
メソッドによって色の名前がブロックパラメーターに渡され、順に表示された。
実用的なプログラムを作るときには、上のプログラムのようにyield
メソッドをいくつも書くのは現実的ではない。そのためには、メソッド定義の中で繰り返し処理(や、他のブロック)を使えばいい。
引数ありのブロック付きメソッドの作り方
以下の例は、指定した個数だけ奇数を順に表示するプログラムである。ブロック付きメソッドはmakeodd
という名前とし、繰り返し処理を使ってブロックパラメーターに値を渡すようにしてある。ただし、繰り返しの回数はmakeodd
メソッドの引数として渡すものとする。リスト1.5の例は引数のないメソッドだったが、ここでは引数のあるメソッドであることにも注意しよう。
def makeodd(limit) # makeoddメソッドを定義する
for i in 0..limit-1
yield 2 * i + 1
end
end
makeodd(5){|x| # makeoddメソッドを呼び出す
puts x
}
|
makeodd
メソッドの引数limit
には、繰り返しの回数を渡す。for
文によって、0からlimit-1まで繰り返すので、yield
メソッドはlimit
回実行される。ブロックパラメーターには1から順に奇数が渡される。makeodd
メソッドを呼び出すときに、引数に5を指定しているので、引数limit
の値は5となる。yield
メソッドは5回実行されるので、5回の繰り返しとなる。
実行例は以下の通り。
$ ruby sample006.rb
1
3
5
7
9
$
|
makeodd
メソッドの引数limit
に渡された回数、つまり5回だけ繰り返しが実行される。yield
メソッドによって奇数が1から順にブロックパラメーターに渡され、表示された。
[番外編]既存クラスへのブロック付きメソッドの追加
最後に、おまけとして、既存のクラスにブロック付きメソッドを追加する例も示しておこう。Rubyでは既存のクラスと同じ名前でクラスを定義すれば、クラスにメソッド(インスタンスメソッド)を追加できる。そこで、Integer
クラスにmakeodd
メソッドを追加してみる。ただし、このmakeodd
メソッドは上の例とは異なり、Integer
クラスのオブジェクトの値から、引数で指定された値の間にある奇数を順に取り出すものとする。
class Integer
def makeodd(limit)
start = self.odd? ? self: self + 1 # 偶数なら次の奇数から
limit = limit.odd? ? limit: limit - 1 # 偶数なら前の奇数まで
count = (limit - start) / 2 + 1 # その間にある奇数の個数
for i in 0..count - 1
yield start + i * 2 # 順に奇数を返す
end
end
end
2.makeodd(9){|x|
puts x
}
|
Integer
というクラスはすでに存在するが、同じ名前のクラスを定義すればインスタンスメソッドが追加できる。ここではmakeodd
メソッドを追加した。オブジェクトの値が偶数であっても、引数として与えた上限の値が偶数であっても、正しく動作するように前処理をしているが、基本的には、指定された範囲内にある奇数をブロックパラメーターに渡すだけである。
実行例は以下の通り。Integer
オブジェクトの値は2であり、引数として9を与えているので、2以上、9以下の範囲にある奇数が求められる。
$ ruby sample007.rb
3
5
7
9
$
|
Integer
オブジェクトの値を開始値として、makeodd
メソッドの引数limit
に渡された値までの奇数が求められる。開始値と引数limit
の間に奇数がない場合や、開始値よりも引数limit
に渡した値が小さいときには何も返されない。
本連載では、クラスの取り扱いについてはまだ説明していないが、リスト1.7のコードはそれほど複雑ではないので、ぜひ追いかけてみてほしい。
まとめ
ブロック付きメソッドを利用すると、繰り返し処理によってオブジェクトの要素などを全て処理したり、毎回の処理の際にブロックパラメーターに値を渡してもらったりすることができる。
また、既存のものを利用するだけでなく、ブロック付きのメソッドを自作することもできる。yield
メソッドを使えば、自作のブロック付きメソッドからブロックパラメーターに値を渡せる。yield
メソッドを実行する回数が繰り返し回数となる。
※以下では、本稿の前後を合わせて5回分(第9回~第13回)のみ表示しています。
連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。
9. while修飾子/until修飾子/while文/until文 ― ちょっと便利な繰り返し処理の構文とは?(1)
Rubyに用意されている繰り返し処理構文のうち、while文/until文の基本的な使い方と落とし穴を解説。また、while/until「修飾子」を使った簡潔な記述方法にも言及する。
10. for文 ― ちょっと便利な繰り返し処理の構文とは?(2)
Rubyに用意されている繰り返し処理の構文のうち、for文の基本的な使い方と配列と組み合わせた利用例を解説。範囲や配列を利用して一定の回数だけ繰り返して文を実行する方法や、配列全体を処理する方法を紹介する。
11. 【現在、表示中】≫ ブロック ― ちょっと便利な繰り返し処理の構文とは?(3)
Rubyに用意されている繰り返し処理の構文のうち「ブロック」を使えば、繰り返し処理をより簡潔に書ける。その基本的な使い方と、自作メソッドでの利用例を解説する。
12. クラスとそのコンストラクター/アクセサー/メソッドを定義し利用するには?
Rubyプログラミングの基本中の基本として、クラスの定義から、そのインスタンスの作成・利用、インスタンスメソッドの定義、変数へのアクセスまでを説明する。
13. クラスを継承するには? メソッドの呼び出しをprivate/protectedで制限するには?
オブジェクト指向言語の特長である「クラスの継承」をRubyで実現する方法を解説。スーパークラスのメソッドの呼び出し制限で、Ruby言語特有の内容についても紹介する。