図解 Rubyのメソッド探索
Rubyのメソッド探索アルゴリズムについて、大分わかってきたので、図にまとめてみた。
対象バージョンは1.9。
探索アルゴリズム
オブジェクトのメソッド探索の順序は「初めてのRuby」や「プログラミング言語 Ruby」などに詳しいが、ごく簡単にまとめると、
- オブジェクトの生成クラスCを辿る
- Cのスーパークラスを順々に辿る
- あるクラスC'がモジュールをインクルードしている場合は、モジュールを先に辿ってからスーパークラスを辿る
- C'がモジュールを複数インクルードしている場合は、後でインクルードしたものから先に辿る
となる。
いくつか注意すべき点を。
- 「オブジェクト」には、いわゆる「インスタンス」も「クラス」も含まれる
- インスタンスメソッドの探索も、クラスメソッドの探索も、本質的には同じである。なぜなら、クラスメソッド = クラスの特異クラスのインスタンスメソッドだからだ。つまり、Rubyでいう「メソッド探索」は、「インスタンスメソッド探索」に等しい。
- 特異クラスを考慮した階層図を用いる
- 階層図に特異クラスを組み入れることで、探索アルゴリズムにおいて特異メソッドを特別扱いする必要がなくなる。
サンプル
次のようなクラス、モジュール、インスタンスを作ったとする。
module FMod end module BMod end class Foo include FMod end class Bar < Foo include BMod end bar = Bar.new
このときのクラス階層を図示すると以下のようになる*1。
クラスの特異クラスにも継承関係があること、また、BasicObjectの特異クラスのスーパークラスがClassクラスであることに注意。
インスタンスbarのメソッド探索と、クラスBarのメソッド探索は、上述のアルゴリズムに基づき、それぞれ次の図の赤矢印と青矢印のように行われる。
ObjectクラスとKernelモジュールのインスタンスメソッドは、いわゆる「インスタンス」、「クラス」の両方から呼び出せる; また、「クラス」からは、Objectクラスのインスタンスメソッドとクラスメソッドの両方が呼び出せる、といったことがわかる。
実行例1: barのメソッド探索
class << bar def im_a p "im_a: bar'" end end class Bar def im_a p "im_a: Bar" end def im_b p "im_b: Bar" end end module BMod def im_a p "im_a: BMod" end def im_b p "im_b: BMod" end def im_c p "im_c: BMod" end end class Foo def im_a p "im_a: Foo" end def im_b p "im_b: Foo" end def im_c p "im_c: Foo" end def im_d p "im_d: Foo" end end module FMod def im_a p "im_a: FMod" end def im_b p "im_b: FMod" end def im_c p "im_c: FMod" end def im_d p "im_d: FMod" end def im_e p "im_e: FMod" end end class Object def im_a p "im_a: Object" end def im_b p "im_b: Object" end def im_c p "im_c: Object" end def im_d p "im_d: Object" end def im_e p "im_e: Object" end def im_f p "im_f: Object" end end module Kernel def im_a p "im_a: Kernel" end def im_b p "im_b: Kernel" end def im_c p "im_c: Kernel" end def im_d p "im_d: Kernel" end def im_e p "im_e: Kernel" end def im_f p "im_f: Kernel" end def im_g p "im_g: Kernel" end end bar.im_a bar.im_b bar.im_c bar.im_d bar.im_e bar.im_f bar.im_g
実行結果:
"im_a: bar'" "im_b: Bar" "im_c: BMod" "im_d: Foo" "im_e: FMod" "im_f: Object" "im_g: Kernel"
実行例2: Barのメソッド探索
class Bar def self.cm_a p "cm_a: Bar'" end end class Foo def self.cm_a p "cm_a: Foo'" end def self.cm_b p "cm_b: Foo'" end end class Object def self.cm_a p "cm_a: Object'" end def self.cm_b p "cm_b: Object'" end def self.cm_c p "cm_c: Object'" end end class Class def cm_a p "cm_a: Class" end def cm_b p "cm_b: Class" end def cm_c p "cm_c: Class" end def cm_d p "cm_d: Class" end end class Module def cm_a p "cm_a: Module" end def cm_b p "cm_b: Module" end def cm_c p "cm_c: Module" end def cm_d p "cm_d: Module" end def cm_e p "cm_e: Module" end end class Object def cm_a p "cm_a: Object" end def cm_b p "cm_b: Object" end def cm_c p "cm_c: Object" end def cm_d p "cm_d: Object" end def cm_e p "cm_e: Object" end def cm_f p "cm_f: Object" end end module Kernel def cm_a p "cm_a: Kernel" end def cm_b p "cm_b: Kernel" end def cm_c p "cm_c: Kernel" end def cm_d p "cm_d: Kernel" end def cm_e p "cm_e: Kernel" end def cm_f p "cm_f: Kernel" end def cm_g p "cm_g: Kernel" end end Bar.cm_a Bar.cm_b Bar.cm_c Bar.cm_d Bar.cm_e Bar.cm_f Bar.cm_g
実行結果:
"cm_a: Bar'" "cm_b: Foo'" "cm_c: Object'" "cm_d: Class" "cm_e: Module" "cm_f: Object" "cm_g: Kernel"
*1:階層図の作成においては、http://idm.s9.xrea.com/ratio/2008/12/14/000803.htmlの図4を参考にした。