読者です 読者をやめる 読者になる 読者になる

Yamakichi’s blog

yamakichiの技術ブログ

メタプログラミング まとめ

メタプログラミングRuby 第2版 を読んで学んだことをまとめておく。

メタプログラミングの中身

メタプログラミングとはコードを記述するコードを記述することである。

Rubyはほぼ全てがオブジェクトでできている、純正オブジェクト指向の言語。

オープンクラス

Rubyは一度定義したclassをオープンし、メソッドをを追加、変更を加えることができる。これをオープンクラスと言う。
オープンクラスは、Rubyの標準クラス(StringやNumeric etc...)に対しても行えて便利である反面、危険な部分もある。
それは既に定義されているメソッドの動きを書き換えることも容易に行えるからだ。
これは「モンキーパッチ」という蔑称で呼ばれていたりもする。

オブジェクトモデル

・ローカル変数とインスタンス変数
ローカル変数はメソッドの中だけ、インスタンス変数はクラスの中で使われる
インスタンス変数は値が代入された時に出現する、なので同じクラスのオブジェクトでもインスタンス変数の数が違っていたりもする。

・変数とメソッド
オブジェクトは、変数の入れ物である。インスタンス変数とは、そのオブジェクト自身の状態を表すものだ。
メソッドはそのオブジェクト自身の振る舞いを定義している。
オブジェクトにはメソッドはない。メソッドはオブジェクトに紐付いたクラスに存在する。

classもオブジェクトだ。 class名やmodule名は定数

・メソッド探索の仕組み
Rubyを扱う者ならメソッドを見つけ出す仕組みを理解しておくべきだ。
レシーバと継承チェーン
メソッドの呼び出し先のことをレシーバと呼ぶ。

class Hoge
    def Hello
        puts  "へろう"
    end
end

obj = Object.new
obj.hello   #=> へろう

これだとobjがレシーバでhelloがメソッド
Rubyの継承は最終的にBasicObjectに到達する。

メソッドを呼び出された時Rubyはまずレシーバに紐づくクラスにメソッドを探しに行く、
そこで見つからなかった場合そのクラスの親クラス(super class)に探しに行く。
これをBasicObjectまで続けていく。
メソッドが見つかればそれが実行される。
BasicObjectまで探しても見つからなかった場合、今度はmethod_missingをレシーバのクラスからもう一度探し直す。
という動きをしている。

・モジュール
モジュールをincludeやprependすることをミックスインと言う。
classでモジュールをincludeした時、そのモジュールは呼び出し先クラスの上の継承階層に挿入される

メソッド

Rubyのメソッドは様々な魔術を行うためのテクニカルな動きをするメソッドが多く存在する。

・send
sendメソッドを用いることによってRubyでは動的にメソッドを呼び出すことができる、
これを動的ディスパッチという。
Object#sendメソッドはとても強力だ。Rubyの本来のルールを壊してしまうことも可能だからだ。
sendを用いることによってclassで定義されているprivateメソッドさえも呼び出すことができる。
カプセル化のルールをいとも簡単にこわしてしまうのだ。
しかし多くのRubyプログラマはこのsendメソッドネカティブに捉えていない。
むしろ、sendメソッドはprivateのメソッドも呼び出せるという特徴があり有効活用するために用いている。ポジティブに捉えている

・method_missing
method_missingをオーバーライドすると、実際には存在しないメソッドを呼び出せる。

ブロック Proc lambda

ブロックとはdo ~ endまたは、{}で囲まれた処理のまとまりのこと、
1行のブロックは{}で、複数行にまたがる処理はdo ~ endで書くという慣習がある。
ブロックを定義できるのはメソッドを呼び出す時だけ。
ブロックはメソッドに渡され,
メソッドはyieldキーワードを使ってブロックをコールバックする。
ブロックをオブジェクトに変換するのがProcクラス
Proc.newにブロックを渡すことでオブジェクトを生成できる。

obj = Proc.new { |a| a + 1 }
obj.call # => 3

オブジェクトを評価するにはProc#callで呼び出すことができる。

Procと似たオブジェクトを生成するメソッドにlambdaがある。
ProcとlambdaはどちらもProc.classのオブジェクトを生成するが、2つ違うところが存在する。
・lambdaはブロックの引数の数に厳密
・returnやbreakを行った場合の挙動が違う。

詳しくはこの記事を参考に。
qiita.com