From: "takeru sasaki" <sasaki.takeru / gmail.com>
Subject: [ruby-list:45298] グローバルにdefしたメソッドのスコープ
Date: Wed, 6 Aug 2008 19:25:34 +0900

るびきちです。

とりあえず、xmpfilterによる注釈形式で途中の値を確かめましょう。

class Foo
  def bar(a,b)
    "Foo#bar(#{a},#{b})"
  end
  def baz
    hello(3,4)      # => "Foo#bar(3,4)"
    Poo.hello(7,8)  # => "Poo.bar(7,8)"
  end
end

def bar(e,f)
  "global bar(#{e},#{f})"
end

def hello(c,d)
  # globalのbarではなくFoo#barが呼ばれる
  # Object#helloは二度呼ばれているので「=> 1回目の値, 2回目の値」と注釈されている
  self              # => #<Foo:0x82da890>, main
  bar(c,d)          # => "Foo#bar(3,4)", "global bar(1,2)"
end

class Poo
  def self.bar(i,j)
    "Poo.bar(#{i},#{j})"
  end
  def self.hello(g,h)
    # Poo.barが呼ばれる
    bar(g,h)        # => "Poo.bar(7,8)"
  end
end

f2 = Foo.new
f2.baz              # => "Poo.bar(7,8)"
hello(1,2)          # => "global bar(1,2)"
method(:hello)      # => #<Method: Object#hello>
method(:bar)        # => #<Method: Object#bar>


> 下記プログラムを実行したときの「globalのbarではなくFoo#barが呼ばれる」部分はなぜそうなるのでしょうか。
> 直感的には「globalのbar」が呼ばれそうに思います。

グローバル関数は、実はObjectクラスのプライベートなインスタンスメソッドです。
つまり

class Object
  private
  def bar(e,f)
    "global bar(#{e},#{f})"
  end

  def hello(c,d)
    bar(c,d)
  end
end

と同じです。
prviateになっているのは、関数的メソッド呼び出ししか許さないようにするためです。
だからどう見ても関数です。

しかし、Foo#baz内でhelloを呼ぶと、Foo#helloが定義されていないので、
グローバル関数のObject#helloを呼びます。注釈にあるように、Object#hello内での
selfはFooオブジェクトなのでObject#hello内でbarを呼び出すと、まずFoo#barを
探します。定義されていたので、Foo#barが呼び出されました。

ポイントは、グローバル関数はクラスに属するメソッドであることです。

--
rubikitch
Blog: http://d.hatena.ne.jp/rubikitch/
Site: http://www.rubyist.net/~rubikitch/