$Id: method.rd,v 1.4 2003/06/18 23:28:28 aamine Exp $
極めるシリーズその 2、メソッド使いこなしちゃうぜ講座。
よいこのみなさんは知ってるとおり Ruby では
def method_name( arg, arg, opt_arg = default, *rest_args, &block_arg ) 内容… end
でメソッドが定義できる。メソッドとは何か? 関数とは違うのか。 まあ、似たようなもんだと思ってもいいと思う。 オブジェクト指向教条主義の人なら怒るだろうけど、 実際使う時は理論なんてどうでもいいのだ。 動作を理解した後に呼び方がふさわしくないと思うようになったら それから変えればいいんじゃない?
で内容だが、opt_arg
は省略可能な引数。
rest_args
も書かなくていいって点では省略可能だが、
それはあくまで数が不特定であるということから派生している。
&引数は使ったことない人もいるだろうが、イテレータブロックを
実体化する(持ちはこべるようにする)ための引数だ。
例えば次のようにすればイテレータブロックをまるなげできる。
def another yield 'ok' end def meth( &block ) another( &block ) end meth do |i| puts i end
これで 'ok'
が出力される。
まあこのへんは Rubyist なら常識だ。
動的な、というのはプログラマがよく使いたがる(かつ好む)言葉だが、
動的なメソッド呼び出し、というのは理解できるだろうか?
例えば、次のような状況を考えてほしい。
条件によって動作を変えたい。条件は、今は文字列の違いとしようか。
条件分岐と言えば if
だろうってんで、まずは if
で分岐するコード。
if condition == 'hoge' then 動作1 elsif condition == 'fuga' then 動作2 elsif condition == 'nono' then 動作3 などなど
こういうのは case
を使えばもうちょっと楽になるんだった。
case condition when 'hoge' 動作1 when 'fuga' 動作2 when 'nono' 動作3 などなど
これでいいじゃん。問題ないよね?
全然よくない。こんなの美しくないではないか。プログラマたるものは極限ま
で美を追求しなければいかん。それに、分岐が多かったり「動作 n
」の部分が
めちゃくちゃ長かったらどうだろう。構造が非常に把握しにくくなってしまう
ではないか。そういう時に、例えば次のような方法がある。
STR2ID = { 'hoge' => :action1, 'hoge' => :action2, 'hoge' => :action3, 好きなだけ並べる } def action1() 動作1 end def action2() 動作2 end def action3() 動作3 end __send__ STR2ID[ condition ]
さて、ここに出てくる __send__
がこの項の主題だ。__send__
は Object
の
メソッドで、一言で説明すれば「好きなメソッドを呼びだすメソッド」という
ことだ。そりゃ、スクリプトで書けば好きなメソッドを呼び出せるのだが、そ
れには必要なだけのメソッド呼び出しを実際にその場に書いておかねばならな
い。しかし __send__
を使うと、実際に呼び出すメソッドの名前を実行時にう
けとって、それを呼び出すことができる。つまり「どのメソッドを呼び出すか」
をスクリプトを書いた時ではなくスクリプトが実行される時まで延ばすことが
できる。それが「動的」の意味だ。
ちなみに「:action1
」のような記法はシンボルを表す。シンボルってのは、こ
れまた常識だけど、任意の文字列と一対一対応するなにかだ。なにか、とぼや
かしたのは、1.4
までは整数で 1.5
からは Symbol
というオブジェクトだか
らだ。シンボルは特に「名前」を扱う時に使う。文字列と比べてよい点は、
というようなところである。
「動的」を別の観点から見てみると、データをコードに変換するという働きで ある。文字列やシンボルはデータ。自分で書くスクリプトはコード。データは プログラムで変更できるがコードは変更できない。変更できないことでプログ ラムの動作の「広がり」が封じられているわけだが、コードをデータに変えて やるとその封印がとける。つまり、プログラムが「広がれる」ようになる。
ところで、lisp なんかはコードが即データで全てのコードは実行時に変更可 能だ。しかし、それでは lisp は最強の言語か? というとやっぱり違う。必 要なのは動的にしたいところだけ構造的に動的にすることであって、全部まる ごと動的にしちゃうっていうのは意味がない。なぜなら、そんなこと言ったら 機械語があればオッケーじゃないか。任意の数字の並び(データ)を実行させる ことができるんだから。
定型のメソッドってのはよくある。例えばインスタンス変数を返すだけの メソッド。これをちゃんと定義するとすると、
def item() @item end
となるわけだが、普通こういう場合は attr
を使うだろう。
attr :item
これは便利だ。タイプ量も少ないし、うっかり
def item() @itm end
なーんてしてしまう恐れもない。でも、attr
とは違う定型定義は
できないのだろうか。それができる。module_eval
という Module
の
メソッドを使うと、定型(だけじゃないが)メソッドを定義するための
メソッドを定義することができる。
class Module def my_attr( name ) module_eval %- def #{name.id2name}() @#{name.id2name} end - end end
この my_attr
は attr
と同じ機能(ただし読み出しだけ)を持つメソッドだ。
このメソッドを定義しておけば好きなモジュール定義の中でこのように使える。
class A my_attr :item end
え、クラス定義の中で文を実行できるのを知らなかった? それはいけないなー。
ついでに今、おぼえておこう。クラス定義ってのは実は定義というより
「クラス登録文」っていうような意味で、実は中身はすべて実行される
ものなのだ。def
は同様にメソッド登録文である。ちなみに、外と中身では
何が違うかというと、環境が違うのだ。環境とは、self
とか type
とかの
ことである。クラス・モジュール定義の中では、まさにその定義中のクラスが
self
になっている。
ではこういう「メソッド定義をするのためのメソッド」を定義する方法を解説
していこう。まず確認しておくと「%-...-
」はただの文字列だ。"..."
でもい
いが、それだと中に「"
」自体を入れることができないので用心のために%文
字列を使っている。使いたいんなら「”」だろーが「’」だろーがヒアドキュ
メントだろうが、好きなものを使えばいい。ようするに文字列ならいいのだ。
つまり、module_eval
の一般的な使用例はこう。
module_eval
文字列
それから引数の name
は、使い方を見てのとおりシンボルを想定しているのだっ
た。つまり name.id2name
でシンボルが文字列に戻る。それを埋めこんで、定
義したいメソッド(の文字列)を作る。その文字列を module_eval
すると、そ
れが実行されたモジュールの定義の中にその文字列が書いてあるかのように反
応する。今回のmy_attr
では文字列の中身はどうなっていたかというと、
def #{name.id2name}() @#{name.id2name} end
def item() @item end
こんなふうになるのだった。で、これをクラス A
の定義の中の、
my_attr
を呼んだところにそのまま展開すると…
class A def item() @item end end
こうなる。ここまで来れば A
に item()
が定義されるのは納得できるだろう。
あとは文字列操作だ。文字列なんだから、好きなように加工して最後に
module_eval
してやればいろいろなことができる。どんなことができるか?
それは、おれが例を挙げなくたって優秀なプログラマならいくらでも実践でき
るはずだ。
実は module_eval
にはまだ使い道がある。それは行番号の変更だ。
ともかく次のコードを見てくれ。
module_eval 'raise', 'another.rb', 200
これを実行すると表示はどうなるか、試してほしい。あとは自分で好きなよう
に使おう。「Rubyスクリプト埋め込み系」のアプリケーションでは役に立つこ
と間違いなしだ。ちなみに instance_eval
も同じ機能を持っている。
Ruby では module_
という名前はなにかと裏技が多いらしい。
module_function
は Module
のメソッドで、普通は以下のように使って
モジュール関数を作るのだが…
def foo # とりあえず普通に定義して いろいろする end module_function :foo # モジュール関数に変える
実はこんな使い方もあったりする。
module_function def foo # 勝手にモジュール関数になる いろいろ end def bar # 勝手にモジュール関数になる いろいろ end
ただし undocumented
なので気をつけてくれ。