1|2|3分かりにくいが便利なProcとlambda(ラムダ)手続きをオブジェクト化ここまでで、メソッドに手続きそのものを渡す方法としてのブロックを紹介しました。実は、「Proc」クラスや「lambda(ラムダ)」という仕組みを使うことで、ブロックとして書いた手続きそのものをオブジェクト化して引き回す方法があります。block03.rbに、その例を示しましょう。block03.rbmeowquackoinkbowwowcluckblock03.rbの実行結果1行目はProc.newを使う方法で、2行目はprocメソッドを使う方法、3行目はlambdaメソッドを使う方法です。Proc.newはprocと比較して、よりシンプルに書けるという以外の違いはありません。4・5行目は「lambda」メソッドの別の書き方で、引数のカッコは省略もできます。どの記法を利用してもProcクラスのオブジェクトが生成されるので、そのまま変数に代入できます。これらの方法で作成したProcオブジェクトは、callメソッドによって7〜11行目のように呼び出せます。callメソッドに与えた引数が、そのままブロック引数に対応します。メソッドに与えるブロックとProcオブジェクトの関係を理解するために、メソッドに与えたブロックをProcオブジェクトとして利用したり、Procオブジェクトをブロックとして与えたりする例をblock04.rbに示します。=>meowblock04.rbの実行結果repeat1メソッドはblock01.rbにおけるrepeatと全く同じです。repeat2メソッドは、引数として「&(アンパサンド)」から始まる「procedure」を用意しています。このように、「&」から始まる引数を定義すると、メソッドに与えられたブロックをProcオブジェクトに変換したものを変数として受け取ることができます。15行目で変数「procedure」にProcオブジェクトを代入し、「repeat1」の引数として「&」を付加して利用しています。このように記述すると、メソッドに与えたProcオブジェクトを、メソッドの定義元でyieldを用いて呼び出せるようになります。Proc.newとlambdaの違いProc.newもlambdaメソッドもProcオブジェクトを生成しますが、returnの挙動に違いが生じます。block05.rbにその違いを示します。fugablock05.rbの実行結果hogeはProc.newによって生成したProcオブジェクトの中でreturnを記述した場合で、fugaはlambdaによるProcオブジェクトをの中でreturnを記述した場合の例になります。実行結果より、Proc.newの場合はreturnに出会った瞬間に、Procオブジェクトが生成されたメソッドを抜けるという動作になっていることが分かります。対するlambdaの場合は、returnが実行されると単にブロックを抜け、以下に続く処理が実行されていることが分かります。lambdaによって生成されたProcオブジェクトは、Proc.newによって生成されたProcオブジェクトよりもメソッドに近い動作を行うといえます。補足「procとlambda、どちらを使えばよいか」ここまで紹介したように、Procオブジェクトを生成するためには、さまざまなやり方が存在します。procとlambda、どちらを使えばよいのか迷うことになります。それらの違いが問題とならない場合はどちらでも良いのですが、プロジェクトの慣例に倣うのが無難でしょう。筆者の場合、もし自分で選べる状況であれば、よりスマートに書けるlambdaの「->」記法を使うようにしています。そもそも、lambdaが生まれた背景には、ErlangやHaskellなどの関数型言語の「ラムダ式」という仕組みがあります。それらの言語でも、ラムダ式を記述する場合に記号として「->」を用います。lambdaの出自が関数型言語なので、それに倣うのは自然なことでしょう。コンテキストを包み込む「クロージャ」Rubyは「クロージャ」という概念を、Procオブジェクトによって実現しています。クロージャとは、ある手続きの引数に指定した変数以外の変数を、その手続きが定義されたコンテキストによって解決する仕組みを指します。言葉で説明すると、どうしても抽象的になって分かりづらいので、block06.rbにクロージャの性質を示す例を挙げてみます。block06.rb555==================51015block06.rbの実行結果これを初めて見る方は、頭が混乱するかもしれません。ややこしいので一つずつ解きほぐしていきましょう。1〜4行目で、「increase」メソッドを定義しています。increaseメソッドは、まず変数「n」を0に初期化して、そのnを使ってn自身に5を足し込むProcオブジェクトを生成して呼び出し元に返しています。6行目の実行では3回とも5が出力されており、11行目の実行では実行されるたびに5が足し込まれています。この違いは、6行目ではincreaseメソッドが実行されるたびにProcオブジェクトが生成され、11行目の実行では、10行目に生成したProcオブジェクトを再利用していることによって起こります。Procオブジェクトを紹介したときに、「これは手続きそのものをオブジェクト化したものだ」と説明しました。本当のところは、「手続きと、その手続きが定義されたコンテキスト(環境)をオブジェクト化したもの」と言った方が正しいです。Procオブジェクトを生成するとき、その手続きのみならずコンテキストまで「包み込んで」オブジェクト化しているのです。先ほどの動作の違いは、この「コンテキストを包み込んでいる」という性質によって説明できます。6行目の実行では実行のたびにProcオブジェクトが生成されているので、おのおののProcオブジェクトが持つコンテキストは別々のコンテキストとなるため5が3回出力されます。対する11行目の実行では、10行目で生成したProcオブジェクトを変数increase_procに代入し、3回callしています。1回目の実行で、increase_procに包み込まれているコンテキストにおける変数nに5が足し込まれます。2回目の実行においても、同様にincrease_procに包み込まれているコンテキストにおける変数nが利用されるので、その値は、5に5を足し込んだ10となります。Procオブジェクトが同一であれば、そこに包み込まれているコンテキストも同一である、というわけです。このような変数のスコープを、「レキシカルスコープ」と言ったりもします。クロージャの仕組みがあるおかげで、eachメソッドなどに与えるブロックの中で安心してローカル変数を使えます。block06.rb@a@b@cblock07.rbの実行結果block07.rbは、ブロックとeachメソッドを組み合わせた典型的な例です。変数prefixはブロックの外で定義されていますが、コンテキストを包み込むクロージャの仕組みのおかげで、eachメソッドの定義元でブロックを実行したとき、意図通りにprefixに「@」が代入されているというわけです。Rubyで一番面白いメタプログラミングを理解するために連載第8回にあたる今回は、今まで何気なく使っていたメソッドやブロックについて掘り下げて解説しました。これらの知識は、クラスやモジュールの知識と合わせて、筆者がRubyで一番面白いと感じるメタプログラミングに通じる基盤となります。次回は、メタプログラミングに入る前に、スレッドや入出力、例外処理など、残りのRubyの基本機能について解説します。優真(Rails技術者認定シルバー試験問題作成者)イタリア、ローマ生まれ。中学生のころHSPに初めて触れ、プログラミングの楽しさを知る。オープンソースやハッカーカルチャーを好む。C#からRubyに転向したときに、動的型付け言語の柔軟性やメタプログラミングの魅力に感動し、Rubyとともにプログラマーとしての人生を歩む決意を固める。学生として所属するかたわら、株式会社アジャイルウェアでプログラマーとして従事。Railsによるコンシューマー向けのWebサービスの開発などに尽力している。好きなメソッドは、define_method。剛司(Ruby業務開発歴7年)兵庫県生まれ。していた言語はJavaとサーバーサイドJavaScript。2007年よりITコンサル会社に転職し、Rubyと出会って衝撃を受ける。Railsで置き換えるプロジェクトに従事。Railsのプラグインやgemも開発。2013年より、株式会社アジャイルウェアに所属。Railsを使って企業向けシステムを構築する業務に従事。転職市場最前線(48):Web業界、今から行くならRubyエンジニアが狙い目?不況で冷え込んでいたIT業界の転職市場に、回復の兆しが見え始めている。業種や職種によって採用数や条件に大きな差異が生まれている。転職市場の動向を追い、自身のキャリア戦略立案に生かしてほしい。1.7.0」が、10月22日にリリースされた。メールマガジンをご購読ください。申し込みページへITmediaはアイティメディア株式会社の登録商標です。