Procクラスとは、処理のまとまりをオブジェクトで表現したものです。Procクラスはコードを見ながらじゃないと理解できないと思うので、まずは以下の例を見てください。
def hello puts 'hello' end
なんの変哲もないメソッドです。hello
メソッドを実行してみるとどうなるでしょうか?
def hello puts 'hello' end hello #=> hello
はい、hello
が出力されるだけです。
Rubyでは、メソッドの実行時にブロックを引数として渡すことができます(「ブロック引数」と呼ばれます)。hello
メソッドの実行時にブロック引数を渡してみるとどうなるでしょうか。
hello do puts 'byebye' end #=> hello
byebyeと出力されそうでしたが、なにも変わりませんね。puts byebye
という処理の塊を実行したいとき、次のように行います。
def hello puts 'hello' yield # 追加 end hello do puts 'byebye' end # hello # byebye
もとのhello
メソッドの中に、yield
メソッドを追加しました。すると無事にbyebyeも出力されています。これはつまり、yieldを書いたところで今回渡したブロック引数が実行されています。
では、yieldの位置を変えてみましょう。
def hello yield #上に移動させた puts 'hello' end hello do puts 'byebye' end # byebyeが先に出力される # byebye # hello
yieldの位置をputs 'hello'
よりも上に移動させました。すると、byebyeのほうが先に出力されています。
&を使ってブロック引数を明示的に渡す
こちらの例をもう一度見てみましょう。
def hello yield puts 'hello' end hello do puts 'byebye' end
helloメソッドを見てみると、引数としてブロックが渡ってくることが明確ではありません。これは使いづらいメソッドです。通常だと、メソッドに引数を渡す場合はメソッド側に引数が渡ることを明示的に定義しますよね。こんな感じで。
def say_love(name) puts "I love #{name}!!" end say_love('Gakky') # I love Gakky!!
ブロック引数も明示的に渡すように定義してみましょう。
def hello(&block) yield puts 'hello' end hello do puts 'byebye' end # byebye # hello
引数として&block
を渡しました。&
をつけることでブロック引数が渡されることを定義できます。&
は慣れない書き方ですが、要するにblock
というブロック引数が渡ってきたと理解すればよいでしょう。block
という名前はなんでもOKです。
が、ここでちょっと問題があります。せっかく&block
という名前で渡しているのに、メソッド内ではどこにも使われている様子がありません。yieldを呼んでいるので実行自体はできていますが、これではまたわかりにくいメソッドになってしまいます。
そこで、yield
の部分をblock.call
に変えてみましょう。
def hello(&block) block.call puts 'hello' end hello do puts 'byebye' end # byebye # hello
call
メソッドを実行することで、ブロック引数を実行できました。引数で&block
と渡ってきたからといって、&block.call
をしてもエラーになってしまうので気をつけてください。
def hello(&block) &block.call # &をつけて実行したらエラーになる puts 'hello' end hello do puts 'byebye' end proc.rb:2: syntax error, unexpected & &block.call
では、引数として渡ってきたblock
のクラス名をputs block.class
で表示させてみましょう。
def hello(&block) puts block.class block.call puts 'hello' end hello do puts 'byebye' end # Proc ← # byebye # hello
block
のクラス名はProcクラスだということがわかりました。処理がまとまったもの、これがProcクラスの正体です。Rubyでは、Procオブジェクトを使って処理の塊を切り分けることができます。では今回の処理をProcオブジェクトとして切り分けて使ってみましょう。
Procオブジェクトを作る
def hello(&block) block.call puts 'hello' end # Procオブジェクトを作っている bye_proc = Proc.new { puts 'byebye' } hello(&bye_proc) # byebye # hello
通常のオブジェクト生成方法と同じように、Proc.new { puts 'byebye' }
をしてbye_proc
オブジェクトを作っています。これは処理の塊であり、ブロック引数(do~end
)として先程まで渡していた処理とまったく同じ内容です。do~end
がオブジェクトになったような感じですね。
引数として渡すときは、やはりhello(&bye_proc)
といったように&
をつけ、Procオブジェクトを渡していることを明示的にする必要があります。