前回のふりかえりと今回の概要
前回は、Rubyを習得する上で役立つ3点セットをご紹介した。すでに、マニュアルを参照したり、Rubyの拡張ライブラリがどう実装されているかなどをソースで確認された方も多いと思う。Rubyはオブジェクト指向言語でもあるので、Java開発者のあなたにはとっつきやすいと感じられる言語ではないだろうか。
ただ、RubyはJavaより後発の言語であり、他言語の長所を取り入れている。もちろん、便利なものは多用される。というわけで、Java開発者のあなたに「知っておくとRubyコードの理解にすぐに役立つもの」をご紹介したい。今回はぜひ押さえておいてほしい文法の「ブロック付きメソッド」を説明する。
ブロック付きメソッド
通常のメソッドは呼び出すときに引数を与えることができる。それと同じように、ブロック付きメソッドは、ブロックと呼ばれるコードを渡すことができる。
Javaのメソッド呼び出しは、
オブジェクト.メソッド(引数...);
Rubyのメソッド呼び出しは、
オブジェクト.メソッド(引数...) #引数がない場合などは(引数)を省略可能
となり、Rubyのブロック付きメソッド呼び出しは、
オブジェクト.メソッド(引数...){ブロック} #1行の場合
または、
オブジェクト.メソッド(引数...)do
ロック #複数行の場合
end
となる。この形はRubyのサンプルを参照するとよく見かける。きっと戸惑われると思うのでじっくり説明したい。
ブロック付きメソッドを使ってみる
まず、メソッドに与えられたブロックを単純に使ってみたい。メソッド内で「yield 」を実行するとブロックを呼び出すことができる。
def foo #fooメソッドの定義
yield #ブロックの実行(1回目)
yield #ブロックの実行(2回目)
end #fooメソッドの定義終わり
foo {puts "Yoo-hoo!"} #fooメソッドをブロック付きで呼び出す
結果は「Yoo-hoo!」が2回出力される。
別のサンプルを試してみたい。ブロック付きメソッドの中では縦棒(|)を使ってyield内の変数をブロック付きメソッドの中で扱うことができる。
def foo
yield("Yoo-hoo!", "Ho-ho!", "He-ho!")
end
foo do |voice1, voice2, voice3|
puts voice1 + voice2 + voice3
end
結果は、yieldで渡した変数が表示される。
ブロック付きメソッドの使われ方
次に、ブロック付きメソッドがよく使われる用途を2つご紹介したい。具体的には、
- ある処理を「繰り返す」
- ある処理を「埋め込む」 である。
この2つの用途を押さえれば、ブロック付きメソッドで記述されたソースをみても理解が早くなると思われる。できるだけシンプルな例を挙げているので、ブロック付きメソッドのイメージをつかんでいただきたい。
まずは、1のある処理を繰り返したい場合について述べる。繰り返しとはいわゆるイテレータである。開発でよく利用されているのではないだろうか。
1. ある処理を「繰り返す」
ブロック付きメソッドを使って、ある処理(文字列の出力)を何回か繰り返す処理を実行してみたい。
5.times{puts "Yoo-hoo!"} #ブロックの内容を5回繰り返す
結果は「Yoo-hoo!」が5回出力される。
別の例を挙げる。配列から要素を取り出して表示するサンプルである。
array = [1,3,5] #配列を作成
array.each do |i| #配列のeachメソッドを呼び出す
puts i
end
結果は配列に格納された数字がそれぞれ出力される。
サンプルのように「ある処理={コード}」をメソッドに渡すことで、処理を繰り返している。このように、Javaと同様にRubyでも、for文を使わずにシンプルな繰り返し処理が記述できる。
2. ある処理を「埋め込む」
次に、処理を埋め込む場合のブロック付きメソッドについて述べたい。まず、こちらはどのような場面で使われるのだろうか。
処理を埋め込むのだから、その前後に決まった形の処理を記述する必要がある場合に使える。一番初めのサンプルを使うと、
def foo
puts "start!" #前処理
yield #ある処理の実行
puts "end!" #後処理
end
foo {puts "Yoo-hoo!!!"}
のように記述できる。実行したい処理の前に行いたい処理と後で行う処理を実行できるだろう。
より具体的な使用例を挙げると、ブロック付きメソッドはいわゆるリソース管理に使える。システムのリソースは有限なので、データベースのコネクションを使用する際などには必ずリソースの取得と使用後の解放を行う必要がある。
Javaでは次のようなtry-catch-finally文を使ってリソースの取得と解放を行っていると思う。
try {
#リソースの取得を記述
}catch(Exception e){
#リカバリ処理を記述
}finally{
#リソースの解放を記述
}
Rubyにおいても例外処理にあたるbegin-rescue-ensure文が存在する。
begin
#リソースの取得を記述
rescue
#リカバリ処理を記述
ensure
#リソースの解放を記述
end
ただし、上記方法でリソースを管理していると、単純にクローズを忘れたり、エラーのハンドリングが漏れたりなどが起こりやすい。リソースを効率良く扱うためには、以下のような各種の工夫が必要になるだろう。
- 開発者の教育
- 確実なテスト
- コードレビュー
- ツールによる検証
- フレームワークによるリソース管理
また、性能テストで負荷をかけるとリソース管理周りでボトルネックが発生したり、リソースの解放がされずエラーの原因になる状況によく遭遇する。よって、この処理を外に出して使い回すようにすれば便利である。このために、ブロック付きメソッドを使用している。ネットワークとファイルのリソース管理の簡単なサンプルを見てみたい。
ネットワークに接続してHTTPのドキュメントを表示するサンプルは以下になる。
require "open-uri" #ライブラリの呼び出し
open("http://samplesample.co.jp") { |uri| #http~のドキュメントを取得
puts uri.read #ドキュメントを読み込んで表示する
}
プロキシサーバの設定が必要な環境では、以下のようになる。
require "open-uri"
open("http://samplesample.co.jp", :proxy => 'http://address:port') { |uri| #address→プロキシサーバのIPアドレス、port→ポート番号
puts uri.read #ドキュメントを読み込んで表示する
}
また、テキストファイルの内容を出力するサンプルは、以下のようになる。
File.open("sample.txt") {|file| #sample.txtをオープン
while words = file.gets #終端まで各行を読み込む
puts words
end
}
サンプルからわかるように、引数で使いたいリソース名を渡し、ブロック内で行いたい処理を記述している。ネットワークやファイルリソースオープンとクローズはメソッド定義側で記述されているので、メソッドを使う側はリソースのオープンとクローズ処理を書く必要はない。
いかがだろうか。ブロック付きメソッドはイテレータにとどまらない使い方をした。なので、ブロック付きメソッドをはじめて目にした方は何のために使われるものか迷ってしまう可能性があった。なお、今回は上記1と2を分けて説明したが、Rubyのブロック付きメソッドは0回以上の繰り返し処理を抽象化したものと表現することもできる。
今回のまとめ
今回はブロック付きメソッドついてご説明した。ブロック付きメソッド自体はJavaにはないものなので、順番に述べさせていただいた。ブロック付きメソッドのサンプルはたくさんあるので参考にして、Rubyになじんでいただきたい。初めはとっつきにくいように見えるだけである。使い始めると、きっと手放せなくなるであろう。
次回は、Rubyの特徴的な文法であるmix-inについてご紹介する。その他の文法についても簡単に触れる。