オープンソースのオブジェクト指向プログラミング言語「Ruby」の文法を一から学ぶための入門連載。最新版の2.1に対応しています。今回は、Rubyの「黒魔術」といわれるメタプログラミングの概要、self、特異メソッド、特異クラス、オープンクラス、モンキーパッチなどの使い方をコード例を交えて解説します。
連載第10回の「RubyのFile/IOクラスで入力と出力、ファイルの読み取りと書き込み、フィルター作成」、第11回の前回「RubyのThread、Fiber、Kernel、forkで並列処理やプロセスの深淵へ」では、データの入出力やスレッド、ファイバー、プロセスといったトピックについて解説しました。これらの知識を活用することにより、データの入出力を自在にコントロールしたり、複数のタスクをスレッドやプロセスといった単位で分割処理を行ったりすることができます。
連載第12回の今回は、RubyをRubyたらしめる、「メタプログラミング」という概念について解説します。込み入った話になるので少し難しいかもしれません。ですが、メタプログラミングとはどういうものかを知っておくことは、初心者からステップアップするためには避けて通れない道です。
メタプログラミングには、さまざまなパターンがあります。メタプログラミングという題材で一冊の本になってしまうほどです。ここでは、数あるメタプログラミングのパターンを理解するための土台となる、selfや特異クラス、特異メソッドといったトピックについて解説します。そして最後に、いくつかの簡単なメタプログラミングのパターンを紹介します。
そもそも、メタプログラミングとは何でしょうか? C#やJavaといった言語を学んだ方はあまりなじみがないかもしれません。また、C++を学んだ方は、テンプレートを利用したメタプログラミングをご存じの方もいると思います。
映画やゲーム、漫画などの作品で、作中の登場人物ではなく視聴者に語りかけてくるようなセリフを「メタ発言」などと言います。このように、「メタ」という言葉には、より上位の存在や抽象概念といった意味があります。
「メタプログラム」とは、大ざっぱに言うと「コードを生成するコード」のことを指します。もう少し詳細に言うと、「より抽象的な記法を使ってコードを書くことにより、動的にコードを生成するコード」です。そして、メタプログラミングはメタプログラムを書くことを言います。メタプログラムでは、どのようなコードが実行されるかは実行時に決まります。
言葉では分かりにくいので、簡単なコード例を使って説明します。
method_name = ARGV[0] define_method method_name do puts "This method is named #{method_name}." end alice
$ ruby meta_programming_01.rb alice This method is named alice. $ ruby meta_programming_01.rb white_rabbit meta_programming_01.rb:7:in 「<main>': undefined local variable or method 「alice' for main:Object (NameError)
では、コードを確認していきましょう。
まず1行目で、「ARGV」を使って、コマンドライン引数に与えられた1番目の文字列を、「method_name」という変数に格納しています。
3〜5行目では、「define_method」という見慣れないメソッドが登場しています。「define_method」は、その名の通り、「メソッドを定義するためのメソッド」です。「hoge」というメソッドを定義するときの、「def hoge〜end」というような記法のメタ版です。
「define_method」の引数には、定義したいメソッドを表す文字列やシンボルを渡します。そして、ブロックの中に定義したメソッドで行いたい処理を記述します。3〜5行目で、コマンドライン引数に与えられた名前のメソッドを定義し、4行目の「puts」で、そのメソッド名を使ったメッセージを出力しています。
6行目では「alice」というメソッドを単純に呼び出しています。
実行例の1行目では、コマンドライン引数に「alice」という文字列を与えているので、きちんとメッセージが出力されています。3行目の実行例では「white_rabbit」という文字列を与えているため、「undefined local variable or method」、つまり「white_rabbitというローカル変数またはメソッドが定義されていない」というエラーが出ています。
ここで、3行目の「define_method」で定義されるメソッド名は、実行時まで分からないことに注目してください。なぜなら、どのような文字列がコマンドライン引数によって与えられるかは、実行される瞬間まで分からないからです。
「define_method」を利用した場合、通常の「def」によるメソッドの定義よりも、何となく小難しく見えるかと思います。それは当然の感覚で、より抽象的な書き方をしているので難しく感じるのです。小学校で習うような足し算や引き算は、「右手にりんごが1個、左手にりんごが2個、合わせて3個」のように具体的に想像できるので簡単ですが、大学で学ぶような微分積分などの解析学や圏論といった数学は非常に抽象的で難しいのと同じ理由です。
その抽象度から、メタプログラミングを乱用すると、たちまち誰も読み解けない(1週間後の自分ですら!)スパゲッティコードが出来上がってしまいます。半面、抽象的であるが故に、似たようなコードを抽象的にスッキリまとめて書ける、既存のクラスを自在に拡張できるなどの大きなメリットがあります。
実際に、「Ruby on Rails」「RSpec」といったライブラリでは、メタプログラミングによる黒魔術を効果的に利用しています。
Rubyではカジュアルにメタなコードを書くことができますが、「本当に、これはメタに書くべきか?」という自問を常に怠らないようにしましょう。
Copyright © ITmedia, Inc. All Rights Reserved.