図解 Rubyのメソッド探索

Rubyのメソッド探索アルゴリズムについて、大分わかってきたので、図にまとめてみた。
対象バージョンは1.9。

探索アルゴリズム

オブジェクトのメソッド探索の順序は「初めてのRuby」や「プログラミング言語 Ruby」などに詳しいが、ごく簡単にまとめると、

  1. オブジェクトの生成クラスCを辿る
  2. Cのスーパークラスを順々に辿る
    1. あるクラスC'がモジュールをインクルードしている場合は、モジュールを先に辿ってからスーパークラスを辿る
    2. 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を参考にした。