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使わなくてもメソッドを定義すればいいんじゃなーいってなる。実際このような使い方をするならそのとおりである。

ここまで前置き。
ここから本題。

続きを読む