InfoQ

InfoQ

News

マイブックマーク

ブックマークするためにログイン または 会員登録 する

ブックマークされました!

ブックマークがエラーになりました。もう一度お願いします。

Ruby 1.9 のSymbol#to_procと(入ったばかりの)Procのカリー化

作者 Werner Schuster , 翻訳者 角谷 信太郎 - (株)永和システムマネジメント 投稿日 2008年3月4日

セクション
デベロップメント,
設計/アーキテクチャ
トピック
Ruby ,
プログラミング
タグ
言語 ,
言語特性
Ruby 1.9での目玉機能といえばスレッド機構の変更やFiber(source)だが、これ以外にも標準ライブラリに加えられた便利な機能がある。Object#tap(source) については既に紹介した通りだ。tapメソッドはメソッドコールのチェインの途中に処理を割り込ませられる便利な機能だ。

tapメソッドがそうであったように、to_procメソッドのアイデアも、少し前からRubyコミュニティでは知られていた。to_proc相当の機能は、Ruby 1.9より前のバージョンでも利用できたのだ。Ruby 1.9 はこの機能をそのままSymbolクラスへと統合した。このおかげで、Symbol#to_procを使うのにライブラリは必要なくなった。


Reg Braithwaite による手短なSymbol#to_procの紹介記事では (1..100).inject(&:+) のようなコードが動くようになったと説明(source)されている。
&演算子はProcオブジェクトをブロックへ変換し、ブロックをProcオブジェクトへ変換します。この場合、&はシンボル :+ をブロックへ変換しようとします。ブロックへの変換にはRuby組み込みの型変換が利用されます。ブロックへの型変換ではまず、Procオブジェクトが与えられているかをチェックします。ブロックが与えられていなければ、引数として与えられたオブジェクトをProcへと変換すべく to_procメソッドが呼び出されます。このとき、シンボル :+ に to_proc メソッドが定義されていれば、それが呼び出されます。Ruby 1.9では、Symbol#to_roc が定義されています。このto_procメソッドは、Procオブジェクトを返します。
つまり、 &:+{ |x, y| x + y }になるということです。

この振る舞いのおかげで次のようなコードを書けるようになる。                                             

plus = :+.to_proc
puts plus.call(1,2) # '3'が表示される

Symbolクラスにto_procメソッドがあるということは、このやり方があらゆるシンボルに通用するということだ。
to_s = :to_s.to_proc
to_s.call(42) # 文字列"42"が返ってくる

 to_procの実装はシンプルだ。Dave Thomas(PragDave)はその動作(source)をこう説明している。                               

def to_proc
 proc { |obj, *args| obj.send(self, *args) }
end

to_procメソッドはProcを生成します。生成されたProcは、オブジェクトを引数にして呼び出されると、渡されたオブジェクトに対してsendメソッドを呼び出します。sendメソッドの第1引数のシンボルは、to_procのレシーバの自分自身です。ですから、たとえば names.map(&:upcase)の場合、namesに格納されている字列をイテレートするわけですが、その際にはnamesの各要素に対してupcaseメソッドを呼び出すブロックがコールバックされます。

この機能のおかげでコードがとても簡潔になるのだが、ひとつ疑問が残る。つまり、これでコードは読みやすくなるのだろうか?これに対する答えはたぶん to_proc がRuby 1.9の標準となったことで変わっていくだろう。なぜなら、いまやSymbolにはかならずto_procメソッドがあるからだ。以前であれば、Symbol#to_procを実現しようとすれば、自分でコードを書くか、Symbolを再オープンしてto_procメソッドを追加するライブラリを使うしかなった。さらに、こうしたコードを理解するにはSymbol#to_procというイディオムを知っていなければならなかった。これについてはRuby 1.9で公式にサポートされたことで、今後はドキュメントでも言及されていくようになるだろう。

それでもやはり疑問は残る。確かに、
 (1..100).map(&:to_s) 
 (1..100).map{|x| x.to_s } 
よりも短く書ける(この例では5文字短かい)。しかし、これは複雑さを増すに見合ったものだろうか? それともやりすぎだろうか?

 Ruby 1.9のProcには他にも追加された機能がある。Porc#curry がそうだ(といってもsvnリポジトリのごく最近のリビジョン(r15459以降)でなければ使えないが)。ruby-core メーリングリストの議論によれば、Proc#curry の機能(source)とは次のようなものだ。
全然難しいことはなくて、
 proc {|x, y, z| x + y + z }.curry
 は、次のようなprocを返すのと同じです
 proc {|x| proc {|y| proc {|z| x + y + z } } }
メソッド名の由来は「カリー化(Currying)(source)」だ。カリー化とは、

カリー化(currying)とは、計算機科学分野の技法の一つ。複数の引数をとる関数を、引数が「もとの関数の最初の引数」で戻り値が「もとの関数の残りの引数を取り結果を返す関数」であるような関数にすること。

つまりカリー化とは、n個の引数を取るProcを1つの引数だけで呼ぶことができるということだ。この場合は(当たり前だが)処理の結果を返すことはできない――処理を実行するために必要な引数が与えられていないからだ。よって、処理結果の代わりに、(n -1)個の引数をとる新しいProcが返される。これが繰り返されて、Procのコードを処理するのに必要なだけの引数が揃ったら、Procは処理を実行してその結果を返す。

 ruby-core メーリングリストの議論スレッドから利用例を紹介しよう。                                           

plus_five = proc { |x,y,z| x + y + z }.curry.call(2).call(3) 
plus_five[10] #=> 15

補足: plus_five は Procであり、[]演算子はProcではProcを実行するようにオーバーライドされている。

注意: Proc#curry が Ruby 1.9 に追加されたのはつい最近である。試してみるには、最新のsvnリビジョン(r15459以降)を利用しなければならない。

原文はこちらです:http://www.infoq.com/news/2008/02/to_proc-currying-ruby19

特集コンテンツ一覧

GAE開発の落とし穴

Googleのクラウド環境をつかったGoogle App Engineによる開発するにあたり、初めての試みで苦悩する開発者達の経験をもとに、各開発フェーズにあわせて問題点やどう解決したかをご紹介します

イベントレポート:「Coqチュートリアル#1」

去る1月12日、定理証明支援系ツールCoqの初心者向けチュートリアルが開催さ れた(http://kokucheese.com/event/index/23667/)。今後も2月2日 (http://kokucheese.com/event/index/23744/)、2月9日、2月16日と引き続き開 催されていく予定である。本記事では、開催の様子をレポートする。

Javaの未来についてのNeal Gafter氏とのディスカッション

Choosing Options

Neal Gafter氏はOracleによるJava買収の影響に関する議論、Javaにセグメンテッドスタックやメタオブジェクトプロトコルを追加することについての主張、そしてJavaとC#との比較について話をしてくれた。

Google Dartのエッセンス:アプリケーションの構築、スナップショット、Isolate

GoogleはVMをともなう新しい言語であり、JSコンパイラでもあるDartをプレビューした。 InfoQはDartのアプリの構築に貢献する文法の裏側を探った:スナップショット、Isolate、モジュール方式

CSPベースのモデル検査ツール「Process Analysis Toolkit」

本記事ではCSPベースの「マルチドメイン・モデル検査ツール」である、PAT(Process Analysis Toolkit)について紹介する。モデル検査は、形式手法(Formal Method)という方法論を基礎とする技術であり、複雑さが増大しながらも安全性を求められる、現在のソフトウェア開発の状況に対する処方箋の1つとして注目されている手法である。

Jenkinsによる継続的インテグレーションのススメ(4) ~CloudBeesでJenkinsをサービスとして使う~

前回まで、Jenkinsの幾つかの側面に注目して解説をしてきました。シリーズ最後の今回は、Jenkinsをサービスとして使う方法を紹介します。

書籍『抽象によるソフトウェア設計-Alloyではじめる形式手法-』の紹介

Alloyは、MITにて開発された仕様記述言語であり、ツールによる自動解析を使い、インクリメンタルに形式仕様が書けることが特長である。筆者らはAlloy開発者による、Alloyを使った形式手法入門書を翻訳、今夏にオーム社より刊行した。本記事では、Alloyの簡単な概要と、翻訳書『抽象によるソフトウェア設計』(「Alloy本」)を紹介する。

Windows デバイスで開発するタッチユーザーインターフェイス

スマートフォンを中心としたマルチデバイスにおけるタッチユーザーインターフェイスへの対応は、既に必須の項目となりつつある。本記事では、Windows デバイスにおける UX のベースとなっている「メトロ」というデザイン言語を掘り下げながら、既存環境を意識しつつもどのようにタッチユーザーインターフェイス開発に取り組んでいくべきであるかについて解説していく。