4

先週、stackoverflow.comで質問に答えたかったのですが、 irbでいくつかのテストを実行した後、興味深いことがわかりました。

class X
  def ==(other)
    p "X#=="
    super
  end
end

data = [ 1 ]
data.include?(X.new)

ここでは、配列内のすべてのアイテムArray#include?が呼び出されることを期待しています。Fixnum#==したがって、X#==が呼び出されることはなく、デバッグ メッセージが出力されることもありません。

しかし、実際には私のルビー バージョン (REE 1.8.7、MRI 1.8.7、1.9.2、および 1.9.3) では、X#==デバッグ メッセージが出力されます。

私がそれをした場合、trueまたはfalseまたはnilまたはメッセージをObject.new出力することはありません。X#==

しかし、次のFixnum#==ように再定義すると:

class Fixnum
  def ==(other)
    p "Fixnum#=="
    super
  end
end

デバッグメッセージを出力した後に実際に元の実装を呼び出すと、出力されFixnum#==X#==最初に期待したように出力されません。

アップデート

干し草の山を針で切り替えると、さらにクレイジーになります。

data = [ X.new ]
data.include?(1)

以前針でメソッドをX#==呼び出したのにプリントアウトしてしまいます。#==

その背後にある理由を誰か指摘できますか?それとも単に最適化の問題ですか?

4

1 に答える 1

4

したがって、配列の各要素にinclude?送信されます。:==

要素がtruefalse、およびの場合、 toのみであるnilため、等値テストはすぐに失敗します。true==true

Fixnums の場合、たとえば1 == 1.0 # => true. 未知Fixnum#==の引数の場合は礼儀正しく、引数の順序を逆にします。これにより、独自の「数値」型を定義できます。

あなたをさらに混乱させたのは、何が起こっているのかを理解するために、 を再定義し Fixnum#==たことです。呼び出しsuperは元のメソッドを呼び出すのではなく、Object#==代わりに呼び出します。試してみてくださいalias_method_chain(またはprepend、Ruby 2.0 の場合!)

ところで、実際のソースを見るFixnumFixnum、 、BignumおよびFloat. 他の組み込みクラス ( RationalComplex、 などBigDecimal) およびユーザー クラスでFixnum#==は、レシーバーと引数が逆になります。に対してこれを行うという事実には依存しませんRationalが、すべての実装はユーザークラスに対してこれを行います。

于 2013-04-13T04:26:02.590 に答える