5

次のコードはどのように機能し、さらに重要なことに、なぜそのように機能するのでしょうか?

class Example
  def one
    def one
      @value = 99
    end
    puts "Expensive Call"
    @value = 99 # assume its expensive call
  end
end

ex = Example.new
puts ex.one # => "Expensive Call"; 99
puts ex.one # => 99

ここで、 method の最初の呼び出しでone、Ruby は外側のoneメソッドを実行しますが、連続する呼び出しでは、内側のメソッドのみを実行し、外側のメソッドを完全 oneにバイパスします。one

どうしてこうなったのか、どうしてこうなったのか知りたいです。

4

3 に答える 3

7

初めて実行すると、クラス内で自分自身を再定義してから終了します。2 回目は、メソッドone自体が単に@value = 99にオーバーライドされているため、何も出力されません。

于 2012-07-08T16:19:29.670 に答える
7

使い方

classdefは実際には実行可能なコードであるため、Ruby では実行時にクラスを再定義できます。あなたの例では、コードは次のことを行います。

  1. インスタンス メソッドが呼び出されたときに Example#one メソッドを (再) 定義する Example#one メソッドを定義します。
  2. 実際には、外側のインスタンス メソッドが呼び出されるまで、内側の定義は実行されません(理不尽な人はこの定義を正当に主張するかもしれませんが、それはこの議論の目的には関係のないパーサー/インタープリターの詳細に入ります。)
  3. 「ex」という名前の Example のインスタンスを定義します。
  4. exでインスタンス メソッドを呼び出します。これにより、同じ名前の新しいメソッドが定義されます。
  5. インスタンス メソッドを再度呼び出すと、古いメソッドの代わりに新しいメソッドが使用されます。

機能する理由

基本的に、メソッドの最後の定義はその名前空間の以前の定義を置き換えますが、メソッドは実際には新しいオブジェクトです。これは、次のように実際に確認できます。

def my_method
  puts 'Old Method'
  puts  self.method(:my_method).object_id
  def my_method
    puts 'New Method'
    puts  self.method(:my_method).object_id
  end  
end

これを irb または pry セッションで実行すると、実行時にメソッドが再定義されていることがわかります。

> my_method; puts; my_method
Old Method
8998420

New Method
8998360

異なるオブジェクト ID からわかるように、メソッドは同じ名前を持ち、同じオブジェクト (通常はコンソールのメイン) にアタッチされていますが、実際には異なるメソッド オブジェクトです。ただし、メソッドは同じ名前で定義されているため、インスタンスがメソッド ルックアップを実行すると、最新の定義のみが検出されます。

于 2012-07-08T16:30:51.660 に答える
3

Ruby には内部メソッドや外部メソッドなどがないことを最初に認識することが重要です。

メソッド内で新しいメソッドを定義しています。この場合、定義されているメソッドは既存のメソッドと同じ名前であるため、新しい定義は元のメソッドを完全に上書きします。

あなたが持っているものは、(おそらく)より明白なものと同等です:

class Example
  def one
    self.class.send(:define_method, :one) do
      @value = 99
    end
    puts "Expensive Call"
    @value = 99 # assume its expensive call
  end
end

ここでは、クラスのコンテキスト内でメソッドを定義していることは明らかです。

于 2012-07-08T16:33:15.573 に答える