今回は「プログラミングをプログラムする」メタプログラミングについて学びます。メタプログラミングを利用すると,動的にメソッドを追加するなど,実際のアプリケーション作成に役立つ処理が簡単に実現できます。メタプログラミングと小言語の関係についても解説を加えました。
今回はメタプログラミングを扱います。メタとはギリシャ語で「間に,後に,越える」などを意味する接頭辞「meta」に由来する言葉で,「超越」,「高階」などの意味があります。例えば,Rubyをはじめとする多くのオブジェクト指向プログラミング言語では,「クラスのクラス」のことを「メタクラス」と呼びますし,他のオブジェクトを支えるクラス・オブジェクトなどのことをメタオブジェクトと呼ぶこともあります。
メタプログラミングとは,プログラミングをプログラミングすることです。そんなことが何の役に立つのかと感じる方もいらっしゃるでしょう。今回は一見して何の役に立つのか分からないメタプログラミングの能力の一端を紹介します。
メタプログラミング
では,早速Rubyを用いてメタプログラミングの実例を見てみましょう。まずは動的にメソッドを生成する例です。
Rubyのクラスに組み込まれた機能であるattr_accessorは,インスタンス変数にアクセスするメソッドを作り出します(図1)。図1に示した短いコードだけで,Personというクラスに対してnameメソッド,ageメソッドが生成されます。これらを使って代入もできます。
|
図1●メタプログラミングの例 Rubyではattr_accessorを用いてインスタンス変数をアクセスするメソッド(ここでは,nameとage)を生成できる。 |
ここで重要なことは,このattr_accessorはこのような機能を実現する文法ではなく,Moduleクラスが提供する単なるメソッドであり,その気になれば同じような働きをするメソッドを自分で定義できる,ということです。
attr_accessorは内部的に次のような処理を行っています。
(1)引数のシンボルすべてに対して以下の処理を繰り返す。
(2)シンボルで指定された名前のメソッドを定義する。そのメソッドはシンボルの名前の先頭に「@」を付加したインスタンス変数の値を取り出す。
(3)シンボルで指定されたメソッド名の後ろに「=」を付加した名前のメソッドを定義する。そのメソッドは引数を1つ取り,その値をシンボルの名前の先頭に「@」を付加したインスタンス変数に設定する。
簡単な手順のようですが,CやC++のようなプログラミング言語ではなかなかできないことです。なぜなら,C++などではプログラムの実行中,簡単には動的にクラスにメソッドを追加できないからです。Rubyでは図1のように簡単に実現できています。実際にはattr_accessorはCで実装されていますが,同じ処理をRuby自体で実現するならば図2のようになるでしょう。
|
図2●attr_accessorをRuby自体で実装した例 |
class_evalは文字列を受け取って,それをクラスの文脈で評価するメソッドです。図2では「%{」から対応する「}」までの間が文字列としてclass_evalに渡されています。文字列の中では「#{」と「}」で囲むことで式を埋め込むことができますから,変数symで示されるメソッド名が埋め込まれた文字列が評価され,ループの繰り返しごとに2つのメソッドが定義されることになります。ですから,次のような呼び出しがあると,
attr_accessor :name
呼び出しの対象となったクラスにnameという名前でインスタンス変数@nameの値を取り出すメソッドを定義し,name=という名前でインスタンス変数@nameの値を設定するメソッドを定義します。
メタプログラミング機能を持たない言語では,このような処理を実現しようとするとかなり手間がかかります。言語の文法そのものを拡張するか,マクロのようなプログラムを前処理するプリプロセッサを導入するかしかありません。いずれにしても通常の言語にとっては大げさなことになってしまいます。