Procについて
RubyにはProcクラスという組み込みのクラスがあるが、例えばるりまのProcのところを見ると
ブロックをコンテキスト(ローカル変数のスコープやスタックフ レーム)とともにオブジェクト化した手続きオブジェクトです。
とか書いてあってさっぱり意味がわからないのである。そもそも手続きオブジェクトってなんぞや。
まずはブロックから
Procを理解する前に、まずRubyのブロックについてよく知っておいたほうがよい。ブロックというのはdoとendで囲まれたコードであり、例えば以下のようにメソッドに渡すことができる。
3.times do |i| p i end #=>0 #=>1 #=>2
これは、3というFixnumオブジェクトに対してtimesメソッドを呼び、「p i」というブロックを渡している。ブロックの引数は変数iで受け取る。というように解釈する。Rubyにおけるブロックはコードの塊とその他もろもろの情報であり、これをメソッドに渡して、メソッド内から実行するということができる。例えば以下のように書くとFixnum#timesが数字より1回少ないループをするメソッドに変わる。
class Fixnum def times i = 0 while(i < self - 1) do yield i i += 1 end end end 3.times do |i| p i end #=> 0 #=> 1
メソッド側はブロックを受け取ったかどうかをKernel.block_given?で確認することができ、yieldで呼び出すことができる。Rubyではどんなメソッドに対してでもブロックを渡すことができるが、ブロックを使わなければ単に無視されるだけである。
# 二項演算子Fixnum#+にブロックを渡してみる試練 p 1.+3 do p 5 end #=> 4
ところでRubyは「すべてがオブジェクト」とよく言われるが、ブロックはオブジェクトではない。Blockクラスも無い。ブロックは文法的な機能であり、ifやwhileがオブジェクトやメソッドでないのと同様に、ブロックもRubyの構文の一部である。
手続きをオブジェクト化する
Procクラスについて、「手続きをオブジェクト化する」という説明を見ることがある。ここで言う手続きとは、平たく言えばコードのことであり、Rubyにおいてはもっと狭く表現して「ブロックをオブジェクト化する」というように解釈してよい。すなわち、ブロックを受け取ったメソッド内でyieldする以外に呼び出し方法の無かったものが、オブジェクトとしてどこにでも渡すことができるようになる、ということだ。オブジェクト化するので変数に入れることもできる。Procオブジェクトを作るにはProc.newにブロックを渡し、ブロックを実行するときはProc#callを呼ぶ。
hoge = Proc.new do p 'hoge!' end hoge.call #=> hoge!
また、引数を受け取るブロックをオブジェクト化した場合はProc#callに引数を渡せばブロックに届く。
hoge = Proc.new do |i| p 'hoge' * i + '!' end hoge.call(2) #=> hogehoge!
このような使い方をする場合、これは単純な無名関数であり、別にProc使わなくてもメソッドを定義すればいいんじゃなーいってなる。実際このような使い方をするならそのとおりである。
ここまで前置き。
ここから本題。