難しいが強力! Rubyのメタプログラミング、self、特異クラス/メソッド、オープンクラスとモンキーパッチ:若手エンジニア/初心者のためのRuby 2.1入門(12)(3/4 ページ)
オープンソースのオブジェクト指向プログラミング言語「Ruby」の文法を一から学ぶための入門連載。最新版の2.1に対応しています。今回は、Rubyの「黒魔術」といわれるメタプログラミングの概要、self、特異メソッド、特異クラス、オープンクラス、モンキーパッチなどの使い方をコード例を交えて解説します。
オブジェクト固有のメソッド「特異メソッド」
Rubyでは、特定のオブジェクトにのみ有効な、オブジェクト固有のメソッドを定義できます。通常、あるクラスのオブジェクトは、クラスに定義されているメソッドしか利用できません。しかしながら、「特異メソッド」という仕組みを使うと、オブジェクトにメソッドを追加できます。
では、特異メソッドを定義する簡単な例をmeta_programming_05.rbに示します。
array = [] def array.append_randomized_number self << rand(10) end p array.respond_to?(:append_randomized_number) p Array.new.respond_to?(:append_randomized_number) 10.times { array.append_rondomized_number } p array
$ ruby meta_programming_05.rb true false [1, 6, 7, 6, 4, 9, 7, 7, 1, 3]
まず、1行目でarrayという変数に、空の配列であるArrayオブジェクトを格納しています。
3〜5行目がポイントで、ここでarrayに対して特異メソッドを定義しています。特異メソッドを定義するときは、「def {オブジェクト}.{メソッド名}〜end」のように書きます。{オブジェクト}を省略した場合は、前節で説明した通り、「self」に対するメソッド定義となります。
特異メソッドの定義中での「self」は、「array」そのものとなります。ですので、4行目のように「self」を使って「array」自身に、「rand」メソッドで生成した乱数を追加できます。
7、8行目では、「respond_to?」メソッドを利用することで、オブジェクトに「append_randomized_number」が定義されているかを調べています。「respond_to?」は、レシーバーが引数の名前のメソッドを呼び出せるかを調べるためのメソッドです。
実行結果の1行目および2行目から、「array」には特異メソッド「append_randomized_number」が定義されていますが、新しく生成した「Array」オブジェクトには定義されていないことが分かります。
コードの10行目では、10回「append_randomized_number」を呼び出し、11行目でその結果を出力しています。
このように、Rubyではオブジェクト固有のメソッドを定義できます。クラス自体にメソッドを定義する場合は、この仕組みを応用します。
クラスメソッドと「特異クラス」
特異メソッドを理解できれば、クラス自体にメソッドを定義する所まで後一歩です。meta_programming_06.rbに、クラス自体にメソッドを定義する例を示しましょう。
class Rabbit; end def Rabbit.colors [:black, :brown, :white, :mixed] end p Rabbit.colors
$ ruby meta_programming_06.rb [:black, :brown, :white, :mixed]
1行目で、何もメソッドを定義しないような「Rabbit」クラスを定義しています。3〜5行目では、「Rabbit」クラスの特異メソッドとして、「colors」という名前のメソッドを定義しています。「colors」は、色を表すいくつかのシンボルを含んだ配列を返します。
ここで、「Rabbit」クラスのオブジェクトではなく、「Rabbit」クラス自体(何度も言いますが、クラス自体もオブジェクトです!)に特異メソッドを定義していることに注意してください。Rubyでは、クラスメソッドを「クラスオブジェクトの特異メソッド」として定義できます。
クラスメソッドを定義できたのはよいのですが、meta_programming_06.rbの書き方は少し不格好です。「Rabbit」クラスの宣言の中に書くのが自然ではないでしょうか。そこで、また「self」が生きてきます。meta_programming_07.rbに、「self」を利用したクラスメソッドの定義の例を示します。
class Rabbit def self.colors [:black, :brown, :white, :mixed] end end p Rabbit.colors
$ ruby meta_programming_06.rb [:black, :brown, :white, :mixed]
1行目から「Rabbit」クラスの定義が始まり、2〜4行目でクラスメソッド「colors」を定義しています。ここで、「self」は「Rabbit」クラス自身を表していることを思い出してください。「self」の存在によって、エレガントにクラスメソッドを定義できるのです。
もし、クラスメソッドが増えてきた場合、逐一「self.〜」と書くのも冗長な感じがします。そのような場合は、特異クラスという仕組みを使うとよいでしょう。特異クラスを使ったクラスメソッドの定義例を、meta_programming_08.rbに示します。
class Rabbit class << self def colors [:black, :brown, :white, :mixed] end end end p Rabbit.colors
$ ruby meta_programming_08.rb [:black, :brown, :white, :mixed]
2行目に見慣れない記法「class << self」がありますが、これは、「self」(ここでは「Rabbit」クラス)の特異クラスを開きますよ」という宣言です。開いた特異クラスの中で定義したメソッドは、そのオブジェクトの特異メソッド、つまりここでは「Rabbit」クラス自体の特異メソッドとなります。そのため、meta_programming_07.rbと同様、「Rabbit.colors」で、色を表す配列を得ることができます。
self、特異メソッド、特異クラスについて説明した理由
実際のメタプログラミングで利用されるメソッドや、そのパターンはさまざまです。メタプログラミングで使われるメソッドを理解するためには、「self」や特異メソッド、特異クラスについての知識が不可欠となります。そのため、今回はそれらの仕組みについて説明することにしました。
また、クラスメソッドを定義することは、現場のコードでしばしば見られるパターンなのに、実際は複雑であることも理由の一つです。Ruby初心者からすると、「class << self」のようなコードを見たら、ギョッとすることでしょう(私もそうでした)。
実際のメタプログラミングのパターンについては、書籍『メタプログラミングRuby』(アスキー・メディアワークス、Paolo Perrotta:著、角征典:訳)が参考になるでしょう。また、次節でもちょっとした簡単なパターンを紹介します。
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日にリリースされた。