難しいが強力! Rubyのメタプログラミング、self、特異クラス/メソッド、オープンクラスとモンキーパッチ:若手エンジニア/初心者のためのRuby 2.1入門(12)(4/4 ページ)
オープンソースのオブジェクト指向プログラミング言語「Ruby」の文法を一から学ぶための入門連載。最新版の2.1に対応しています。今回は、Rubyの「黒魔術」といわれるメタプログラミングの概要、self、特異メソッド、特異クラス、オープンクラス、モンキーパッチなどの使い方をコード例を交えて解説します。
メタプログラミングの簡単なパターン2つ
ここまで、Rubyのメタプログラミングを理解する上で欠かせない、「self」や特異メソッド、特異クラスといった仕組みについて解説しました。これは、メタなコードを書くためのメソッド(最初に紹介した「define_method」もそうです)を使ったコードを読み書きするために必須の知識です。
ここからは、より実践的な話として、数あるメタプログラミングのパターンのうち、簡単なものをいくつか紹介しましょう。
オープンクラスとモンキーパッチ
C#やJavaなどのオブジェクト指向言語を学んだ人を惑わせる概念の一つに、「オープンクラス」があります。オープンクラスとは、言語仕様として、クラスや継承やミックスインといった手続きなしに、既存のクラスを自在に拡張するようなコードを書けることを示します。また、「モンキーパッチ」とは、オープンクラスの仕組みを使うことで、既存のクラスの動作を上書きすることを言います。
ここで、モンキーパッチの例をpattern_01.rbに示します。その簡潔さと強力さと危険さに震えることでしょう。
class Fixnum def +(v) self - v end def *(v) self / v end end puts 42 + 42 puts 42 * 42
$ ruby pattern_01.rb 0 1
オープンクラスでクラスを拡張する場合は、クラスを定義する場合と同様、「class Fixnum」のように書きます。pattern_01.rbでは、整数を表す「Fixnum」クラスのメソッドをモンキーパッチしています。
3〜5行目は、「+」メソッドを上書きして、「self」で表された自分自身から、引数の「v」を減算したものを返しています。6〜8行目でも同様に、「*」メソッドを上書きして、除算した結果を返しています。
11行目では「+」演算子の結果を出力し、同様に12行目では「*」演算子の結果を出力しています。
これはモンキーパッチの強力さと危険さを端的に表現した例です。「Fixnum」を利用するプログラマー(Rubyプログラマーならほぼ全員でしょう)は「+」メソッドは加算を行うものだと期待して、「+」メソッドを利用します。しかしながら、モンキーパッチによって実際のところは減算が行われてしまうのです。
モンキーパッチは最後の手段だと思って、くれぐれも用法用量を守って、正しく使うことを心掛けましょう。
動的なメソッド定義とクラス操作
最後に、ちょっと込み入ったメタプログラミングの例を紹介しましょう。
最初に紹介した「define_method」による動的なメソッド定義を利用すると、似たような処理を持つメソッド群を、まとめて定義できます。また、「Object.const_set」「Object.cont_get」によって、動的にクラスを生成、取得できます。pattern_02.rbに、動的なメソッド定義とクラス操作の例を示しましょう。
{Cat: "meow", Dog: "woof", Owl: "hoot-hoot", Rabbit: "boo"}.each do |animal, roar| Object.const_set animal, Class.new Object.const_get(animal).class_eval do define_method :speak do |count| count.times { puts roar } end end end Cat.new.speak(2) Dog.new.speak(2) Owl.new.speak(2) Rabbit.new.speak(2)
$ ruby pattern_02.rb meow meow woof woof hoot-hoot hoot-hoot boo boo
さあ、少しややこしくなってきました。まずpattern_02.rbが行っていることを大ざっぱに言うと、『4つのクラス「Cat」「Dog」「Owl」「Rabbit」を動的に定義して、各クラスに対し、鳴き声を出力する「speak」というメソッドを定義する』ということを行っています。
1行目では、クラス名と鳴き声のペアを持つハッシュを作り、「each」メソッドで各ペアに対する処理を始めています。
2行目では、「Object.const_set」というメソッドを使って、「Object」クラスに各動物の名前(Cat、Dogなど)の名前を持つ定数に、クラスオブジェクトをセットしています。この動作によって、動的に新しいクラス(「Cat」「Dog」など)を定義できます。
4〜9行目では、「class_eval」メソッドを使って、各クラスに「speak」というメソッドを定義しています。例えば、「Hoge」というクラスがある場合に、「Hoge.class_eval」メソッドをブロックを渡して呼び出すと、このブロックの中は「Hoge」クラスの世界になります。ブロック内は「Hoge」クラスの世界なので、このブロックの中で定義したメソッドは「Hoge」クラスのオブジェクトが実行できるメソッドとなります。
また、4行目の「Object.const_get(animal)」により、例えば「Object.const_get(:Cat)」なら、「Cat」クラス自身を得ることができます。
5行目では「define_method」メソッドにより、「speak」というメソッドを定義しています。引数を許すメソッドを作る場合は、ブロック変数を利用します。ここでは、ブロック変数「count」が引数になります。
6行目では、引数として与えられた「count」回、動物の名前にひも付けられた鳴き声(「roar」)を画面に出力します。
このように、「define_method」「cont_set」「cont_get」「class_eval」を利用することで、動的なメソッド定義やクラス操作が可能となります。動物の種類が増えた場合でも、1行目のハッシュに項目を追加するだけで済みます。
簡潔かつ変化に強いコードとなった代償として、一見したときに、このコードが何をしているのか理解することが難しくなりました。抽象化においては、いつでも付きまとう問題です。その場合、適量のコメントを添えることも必要ですし、時にはメタプログラミングによる抽象化を諦める方がよい場合もあるでしょう。
メタプログラミングで利用できる道具たち
ここまで、いくつかのメタプログラミングで使われるメソッドを紹介しましたが、他にもいろいろあります。代表的なものをいくつか挙げておきましょう。
- 「send」メソッド
- 「eval」メソッド
- 「instance_eval」メソッド
- 「method_missing」メソッド
- 「alias_method」メソッド
- 遅延評価のための「Proc」オブジェクト
次回は最終回、簡単なgem作成で総まとめ
今回は、Rubyにおけるメタプログラミングについて解説しました。数あるメタプログラミングのパターンを学ぶために重要な「self」、特異メソッド、特異クラスの説明から始まり、メタプログラミングのパターンの例を2つ紹介しました。
今回の解説で、いかにRubyがプログラマーに強力な道具を用意してくれていることが理解できたかと思います。数あるオブジェクト指向プログラミング言語の中でも、Rubyの強力さと自由度は群を抜いており、筆者がRubyを好む理由の一つです。興味を持たれた方は、参考書籍やWebを使って、さらなるRubyの深淵を覗いてみてください。
次回は総まとめとして、簡単なgem作成を通じた、Rubyによるアプリケーション開発について解説する予定です。お楽しみに!
- Rubyで逆ポーランド変換機を作りgem作成&コマンドの使い方
- 難しいが強力! Rubyのメタプログラミング、self、特異クラス/メソッド、オープンクラスとモンキーパッチ
- RubyのThread、Fiber、Kernel、forkで並列処理やプロセスの深淵へ
- RubyのFile/IOクラスで入力と出力、ファイルの読み取りと書き込み、フィルター作成
- Rubyの例外とその捕捉――基本のbegin〜rescue〜endからensure、else、retry、後置rescueまで
- Rubyの面白さを理解するためのメソッド、ブロック、Proc、lambda、クロージャの基本
- Rubyのオブジェクト指向におけるクラスとモジュール、継承、Mixin、アクセス制御の使い方
- RubyのNumericとTimeで数値と時間をさまざまな操作・演算・判定
- RubyのString/Regexpクラスによる強力な文字列操作/正規表現
- RubyのRangeクラスと範囲オブジェクト、範囲演算子、イテレーターの使い方
- Rubyの配列、ハッシュテーブルを表現するArray、Hashクラスの使い方
- Ruby 2.1の基本構文/基本文法まとめ&Pryの使い方
- Rubyプログラミングを始めるための基礎知識とインストール
著者プロフィール
麻田 優真(Rails技術者認定シルバー試験問題作成者)
イタリア、ローマ生まれ。中学生のころHSPに初めて触れ、プログラミングの楽しさを知る。オープンソースやハッカーカルチャーを好む。C#からRubyに転向したときに、動的型付け言語の柔軟性やメタプログラミングの魅力に感動し、Rubyとともにプログラマーとしての人生を歩む決意を固める。
現在は奈良先端科学技術大学院大学で学生として所属するかたわら、株式会社アジャイルウェアでプログラマーとして従事。Ruby on Railsによるコンシューマー向けのWebサービスの開発などに尽力している。
好きなメソッドは、define_method。
Twitter:@Mozamimy、ブログ:http://blog.quellencode.org/
監修者プロフィール
山根 剛司(Ruby業務開発歴7年)
兵庫県生まれ。1997年からベンチャー系のパッケージベンダーで10年間勤務。当時、使用していた言語はJavaとサーバーサイドJavaScript。
2007年よりITコンサル会社に転職し、Rubyと出会って衝撃を受ける。基幹システムをRuby on Railsで置き換えるプロジェクトに従事。それ以来Ruby一筋で、Ruby on Railsのプラグインやgemも開発。
2013年より、株式会社アジャイルウェアに所属。アジャイルな手法で、Ruby on Railsを使って企業向けシステムを構築する業務に従事。
Ruby関西所属。
Twitter:@spring_kuma、Facebook:山根 剛司
Copyright © ITmedia, Inc. All Rights Reserved.
関連記事
- 数々の“スペル”で高度なプログラミング:Rubyの魔術
- 公用語に英語、「再起動」したRubyKaigi 2013が東京で開催
いったん終了していたRubyコミュニティ主催の年次イベントが再開。技術色、国際色を強め、盛況のうちに幕を閉じた - Web業界、今から行くならRubyエンジニアが狙い目?
不況で冷え込んでいたIT業界の転職市場に、回復の兆しが見え始めている。だが、業種や職種によって採用数や条件に大きな差異が生まれている。転職市場の動向を追い、自身のキャリア戦略立案に生かしてほしい。 - Ruby 2.0.0がリリース、大規模化対応の機能などを搭載
生誕20年となる節目を迎えて、プログラミング言語「Ruby」の最新版がリリースされた - 新バージョンで何が変わるのか、Rubyはどこへ向かうのか:まつもと×笹田、Ruby 1.9を語る
- いよいよ始まるRuby 1.9への移行:開発コアメンバが語るRubyの今とこれから(前編)
- Rubyの今後の進化の方向性とは?:開発コアメンバが語るRubyの今とこれから(後編)
- 互換性や脆弱性の問題にどう対応していくのか:Rubyが抱える課題、NaClの前田氏が講演
- Rails Hub情報局:「なんでRubyなんか作った!? 迷惑だ!」に対するMatzの答え
- Rails Hub情報局:Rubyはイノベーション言語として選ばれている
- Rails Hub情報局:Rubyのまつもと氏は、一発屋で終わるのか?
- Rails Hub情報局:Rubyに魔法は要らない
- 数々の“スペル”で高度なプログラミング:Rubyの魔術
- 晴読雨読@エンジニアライフ:『たのしい開発 スタートアップRuby』――なぜRubyistたちはあれほど楽しそうなのか
- 「JRuby 1.7.0」登場、1年半ぶりのメジャーアップデート
JavaVM上のRuby実装「JRuby」の最新版となる「JRuby 1.7.0」が、10月22日にリリースされた。