# Rubyのblock、Proc、lambdaを理解する _published: 2009/01/01_ ![alt](http://b.hatena.ne.jp/entry/image/http://d.hatena.ne.jp/shunsuk/20090101/1230816826) 新年おめでとうございます。2009年の最初のネタはプログラミングのネタにすることにしました。 Rubyについてのステキなエントリーがあったので、紹介します。 - [Understanding Ruby Blocks, Procs and Lambdas - Robert Sosinski](http://www.robertsosinski.com/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/) 私は翻訳能力がないばかりか、リーディング能力も貧相です。ぜひ、原文を読んでみてください。 ## はじめに Rubyの `block` 、 `Proc` 、 `lambda` はパワフルですが、解りにくい。Rubyはクロージャを使う方法が4つあって、それぞれチョットずつ違います。ここでは、そのへんを解説したいと思います。 ## Block もっとも簡単で、かつRubyっぽいと言えば、Blockですね。 ```ruby array = [1, 2, 3, 4] array.collect! do |n| n ** 2 end puts array.inspect # => [1, 4, 9, 16] ``` 何が起こっているか? 1. まず、 `block` 付きで `Array` の `collect!` メソッドを呼びます。 2. `block` が実行されます。変数 `n` が2乗されます。 3. 配列の各要素が2乗されました。 `collect!` は、配列の各要素で `block` 中のコードを使っていることに注意してください。次に、独自の `collect!` メソッドを実装してみます。 `iterate!` メソッドを作ってみましょう。 ```ruby class Array def iterate! self.each_with_index do |n, i| self[i] = yield(n) end end end array = [1, 2, 3, 4] array.iterate! do |n| n ** 2 end puts array.inspect # => [1, 4, 9, 16] ``` `Array` クラスを再オープンして、 `iterate!` メソッドを追加しました。破壊的メソッド(オブジェクトの状態が変わるメソッド)なので、Rubyの習慣に習って `!` を付けています。 `iterate!` メソッドは `collect!` メソッドと同じ動きをします。 メソッドの中で `block` の名前を特定する必要はありません。かわりに `yield` キーワードを使うことができます。 `yield` キーワードはメソッドに与えられた `block` を実行します。そして、 `n` を `yield` に渡すことができます。 1. `Array` の `iterate` を呼びます。 2. `n` (1, 2, ...) 付きで `yield` が呼ばれると、 `block` に数値が渡されます。 3. `block` は数値を受け取って2乗します。最後の値を処理すると、自動的に `return` します。 4. `yield` は `block` の返り値を出力し、配列の値が書き変わります。 5. 配列の各要素の分だけ繰り返されます。 値を2乗したり、文字列に変換したり、スクリーンに出力したり、柔軟な処理ができます。 `yield` ではなく、 `Proc` を使ってみましょう。 ```ruby class Array def iterate!(&code) self.each_with_index do |n, i| self[i] = code.call(n) end end end array = [1, 2, 3, 4] array.iterate! do |n| n ** 2 end puts array.inspect # => [1, 4, 9, 16] ``` 1つ前の例に似ていますが、2つの違いがあります。1つ目は、 `&` のついた引数を与えていることです。2つ目は、 `yield` の代わりに `call` を使っていることです。結果はまったく同じです。しかし、この違いから `block` について学ぶことができます。 ```ruby def what_am_i(&block) block.class end puts what_am_i {} # => Proc ``` `block` は単に `Proc` だったのです!それでは、 `Proc` とは何でしょうか? ## Proc 同じ `block` を何度も使いたいときがあります。そんなときに再利用するためのコードが `Proc` です。 `block` と `Proc` の唯一の違いは、 `block` は保存できないということです。 `Proc` について見てみましょう。 ```ruby class Array def iterate!(code) self.each_with_index do |n, i| self[i] = code.call(n) end end end array_1 = [1, 2, 3, 4] array_2 = [2, 3, 4, 5] square = Proc.new do |n| n ** 2 end array_1.iterate!(square) array_2.iterate!(square) puts array_1.inspect puts array_2.inspect # => [1, 4, 9, 16] # => [4, 9, 16, 25] ``` ## blockが小文字で、Procが大文字の理由 `block` はクラスではなく、 `Proc` はクラスです。そこで、 `block` は小文字で、 `Proc` は大文字で書いています。同じ理由で、 `lambda` も小文字です。 `iterate!` メソッドの引数に `&` を付けない方法に注目しましょう。 `Proc` をを渡すことは、他の型の引数を渡すのと同じです。 `Proc` は他のオブジェクトと同じように扱われます。 ```ruby class Array def iterate(code) self.each_with_index do |n, i| self[i] = code.call(n) end end end array = [1, 2, 3, 4] array.iterate(Proc.new do |n| n ** 2 end) puts array.inspect # => [1, 4, 9, 16] ``` 上記の例は、他の言語がクロージャを扱うのとまったく同じです。でも、Rubyっぽくありませんね。 `block` を単独で使わない場合はどうでしょう?メソッドに複数のクロージャを渡したいときは? `Proc` を使うと、こう書けます。 ```ruby def callbacks(procs) procs[:starting].call puts "Still going" procs[:finishing].call end callbacks(:starting => Proc.new { puts "Starting" }, :finishing => Proc.new { puts "Finishing" }) # => Starting # => Still going # => Finishing ``` `Proc` ではなく `block` を使うのはどんな場合でしょうか? - `block` - メソッドが小分けにできる場合。ユーザーに小分けにした断片を使わせたいとき - データベースのマイグレーションのように、複数の表現を自動的に実行したいとき。 - `Proc` - 1つの `block` を複数回使いたいとき。 - メソッドが1つまたは複数のコールバックをとるとき。 ## Lambda これまでに、 `Proc` を2通りの方法で使ってきました。直接渡す方法と、変数に保存する方法です。これは、他の言語の無名関数や `lambda` によく似ています。 `lambda` はrubyでも使うことができます。 ```ruby class Array def iterate(code) self.each_with_index do |n, i| self[i] = code.call(n) end end end array = [1, 2, 3, 4] array.iterate(lambda { |n| n ** 2 }) puts array.inspect # => [1, 4, 9, 16] ``` 一見すると、 `lambda` は `Proc` と同じに見えます。しかし、2つの微妙な違いがあります。1つ目は、 `Proc` と違って、 `lambda` は引数の数をチェックするということです。 ```ruby # example-9.rb def arguments(code) one, two = 1, 2 code.call(one, two) end arguments(Proc.new { |a, b, c| puts "Give me a #{a} and a #{b} and a #{c.class}" }) arguments(lambda { |a, b, c| puts "Give me a #{a} and a #{b} and a #{c.class}" }) # => Give me a 1 and a 2 and a NilClass # *.rb:10: ArgumentError: wrong number of arguments (2 for 3) (ArgumentError) ``` `Proc` だと、余分な変数には `nil` が入りますが、 `lambda` ではエラーが発生します。 2つ目の違いは、 `lambda` は `diminutive returns` があるいということです。 `Proc` の `return` はメソッドを中断します。 `lambda` は値を返してメソッドを続行します。解りにくいですか?例を見てみましょう。 ```ruby def proc_return Proc.new { return "Proc.new"}.call return "proc_return method finished" end def lambda_return lambda { return "lambda" }.call return "lambda_return method finished" end puts proc_return puts lambda_return # => Proc.new # => lambda_return method finished ``` `proc_return` では、 `return` キーワードがあると、処理を中断します。一方、 `lambda_return` メソッドは `"lambda"` という文字列を返す `lambda` があっても、次の `"lambda_return method finished"` を出力します。なぜ、違うのでしょうか? この違いは、処理とメソッドのコンセプトの違いにあります。 `Proc` はメソッドではなく、コードスニペットです。このため、 `Proc` の `return` は `proc_return` メソッドの `return` になります。 `lambda` はメソッドと同じように動作します。引数の数をチェックし、返り値を返します。 `lambda` は、匿名のメソッドを書くのと同じことなのです。 下のような場合は、 `Proc` の代わりに匿名メソッド(`lambda`)を使う必要があります。 ```ruby def generic_return(code) code.call return "generic_return method finished" end puts generic_return(Proc.new { return "Proc.new" }) puts generic_return(lambda { return "lambda" }) # => *.rb:8: unexpected return (LocalJumpError) # => generic_return method finished ``` `lambda` はメソッドと同じように振る舞いますから、 `return` を使うことができます。この意味の違いは、下の例に現れます。 ```ruby def generic_return(code) one, two = 1, 2 three, four = code.call(one, two) return "Give me a #{three} and a #{four}" end puts generic_return(lambda { |x, y| return x + 2, y + 2 }) puts generic_return(Proc.new { |x, y| return x + 2, y + 2 }) puts generic_return(Proc.new { |x, y| x + 2; y + 2 }) puts generic_return(Proc.new { |x, y| [x + 2, y + 2] }) # => Give me a 3 and a 4 # => *.rb:11: unexpected return (LocalJumpError) # => Give me a 4 and a # => Give me a 3 and a 4 ``` `generic_return` は2つの値を返すクロージャを期待しています。 `lambda` だと簡単ですね。 `lambda` の代わりに `Proc` を使うべきなのはいつでしょうか?これはクロージャをどう見るかですね。 `block` を渡したいというだけなら `Proc` を使います。あるメソッドを別のメソッドに渡し、かつ `return` に意味があるなら `lambda` を使います。 `lambda` がオブジェクトの形をしたメソッドならば、それを既存のメソッドに保存したり受け渡したりできるのでしょうか?Rubyの場合、ちょっとトリッキーです。 ## Method Object 既存のメソッドをクロージャとして他のメソッドに渡すことができます。Rubyの `method` メソッドが使えます。 ```ruby class Array def iterate!(code) self.each_with_index do |n, i| self[i] = code.call(n) end end end def square(n) n ** 2 end array = [1, 2, 3, 4] array.iterate!(method(:square)) puts array.inspect # => [1, 4, 9, 16] ``` この例では、すでにある `square` メオソッドを `iterate!` メソッドに渡しています。このときのオブジェクトの型は何でしょうか? ```ruby def square(n) n ** 2 end puts method(:square).class # => Method ``` 思った通り、 `square` は `Proc` ではなく `Method` です。コンセプトが同じであれば、この `Method` オブジェクトは `lambda` と同じように振る舞うはずです。しかし、 `square` は名前付きのメソッドで、 `lambda` は匿名メソッドです。 `lambda` は `Method` 型なのでしょうか? ```ruby puts lambda {}.class # => Proc ``` `lambda` は Proc でした。なぜ?正直、 `lambda` がなぜ `Method` オブジェクトでないのか解りません。おそらく、Rubyの後のバージョンでは変更されるのではないでしょうか。 ## まとめ Rubyの4つのクロージャである、 `block` 、 `Proc` 、 `lamblda` 、 `Method` を見てきました。 `block` と `Prock` はコードスニペットとして振る舞い、 `lambda` と `Method` はメソッドのように振る舞うことが解りました。コードの例から、どんな場合にどれを使えば効果的か解ったと思います。それでは、表現豊かで面白いRubyの機能を試してみてください。