検索
連載

難しいが強力! Rubyのメタプログラミング、self、特異クラス/メソッド、オープンクラスとモンキーパッチ若手エンジニア/初心者のためのRuby 2.1入門(12)(1/4 ページ)

オープンソースのオブジェクト指向プログラミング言語「Ruby」の文法を一から学ぶための入門連載。最新版の2.1に対応しています。今回は、Rubyの「黒魔術」といわれるメタプログラミングの概要、self、特異メソッド、特異クラス、オープンクラス、モンキーパッチなどの使い方をコード例を交えて解説します。

PC用表示 関連情報
Share
Tweet
LINE
Hatena
「若手エンジニア/初心者のためのRuby 2.1入門」のインデックス

連載目次

※編集部注

本連載はRuby 2.1プログラミングの入門連載です。Ruby on Railsについて学びたい方は連載「開発現場でちゃんと使えるRails 4入門」をご覧ください。


メタプログラミングのパターンを理解するための土台を習得しよう

 連載第10回の「RubyのFile/IOクラスで入力と出力、ファイルの読み取りと書き込み、フィルター作成」、第11回の前回「RubyのThread、Fiber、Kernel、forkで並列処理やプロセスの深淵へ」では、データの入出力やスレッド、ファイバー、プロセスといったトピックについて解説しました。これらの知識を活用することにより、データの入出力を自在にコントロールしたり、複数のタスクをスレッドやプロセスといった単位で分割処理を行ったりすることができます。

 連載第12回の今回は、RubyをRubyたらしめる、「メタプログラミング」という概念について解説します。込み入った話になるので少し難しいかもしれません。ですが、メタプログラミングとはどういうものかを知っておくことは、初心者からステップアップするためには避けて通れない道です。

 メタプログラミングには、さまざまなパターンがあります。メタプログラミングという題材で一冊の本になってしまうほどです。ここでは、数あるメタプログラミングのパターンを理解するための土台となる、selfや特異クラス、特異メソッドといったトピックについて解説します。そして最後に、いくつかの簡単なメタプログラミングのパターンを紹介します。

Rubyの黒魔術「メタプログラミング」とは

 そもそも、メタプログラミングとは何でしょうか? C#やJavaといった言語を学んだ方はあまりなじみがないかもしれません。また、C++を学んだ方は、テンプレートを利用したメタプログラミングをご存じの方もいると思います。

 映画やゲーム、漫画などの作品で、作中の登場人物ではなく視聴者に語りかけてくるようなセリフを「メタ発言」などと言います。このように、「メタ」という言葉には、より上位の存在や抽象概念といった意味があります。

 「メタプログラム」とは、大ざっぱに言うと「コードを生成するコード」のことを指します。もう少し詳細に言うと、「より抽象的な記法を使ってコードを書くことにより、動的にコードを生成するコード」です。そして、メタプログラミングはメタプログラムを書くことを言います。メタプログラムでは、どのようなコードが実行されるかは実行時に決まります。

簡単なコード例

 言葉では分かりにくいので、簡単なコード例を使って説明します。

method_name = ARGV[0]
 
define_method method_name do
  puts "This method is named #{method_name}."
end
 
alice
meta_programming_01.rb
$ 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)
meta_programming_01.rbの実行例

 では、コードを確認していきましょう。

 まず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.

ページトップに戻る