Tech Racho エンジニアの「?」を「!」に。
  • 開発

Ruby: 4種類の同等性比較: equal?/eql?/==/===(翻訳)

概要

原著者の許諾を得て翻訳・公開いたします。

Ruby: 4種類の同等性比較: equal?/eql?/==/===(翻訳)

Rubyのさらなる特異な性質として、同等性の比較があります。さほど複雑ではありませんが、名前に注意しましょう。

同等性比較の4つの概念

1. equal?: オブジェクトの同一性比較

これはわかりやすい概念です。x.object_id == y.object_idのように、2つのオブジェクトが完全に同一であるべきだと考えられます。

2. ==(ダブルイコール): 「同等性」の同等性

これは大抵の場合注意が必要です。2つのオブジェクトは同じとして扱われるべきです。そのクラスで<=>ロケット比較演算子がサポートされている場合、==が同じ2つの値についてtrueを返すことが期待されているならば<=>0を返します。

3. eql?: ハッシュキーの同等性

これは通常==と同じ動作です。eql?は普通オーバーライドされた==メソッドのエイリアスです。

eql?の最も重要な効果は、ハッシュキーを区別するときに発揮されます。ドキュメントには以下のように書かれています。

hash値が同一かつ2つのオブジェクトが互いにeql?の場合、2つのオブジェクトは同じハッシュキーを参照する。

次は現実的な例です。

1 == 1.0 # => true
1.eql?(1.0) # => false

# すなわち以下の2つは異なるキーとして扱われる
{1: "Idiosyncratic", 1.0 "Hash"}

つまりeql?==より少しばかり厳格です。2つのオブジェクトが同一クラスのインスタンス同士でない場合はfalseを返すためです。典型的な実装は以下のようになります。

def eql?(other)
  instance_of?(other.class) && self == other
end

4. ===(トリプルイコール): 自在な同等性

これはcase文で暗黙に使われます。通常は==と同じ動作ですが、何らかの種類のクラスであるなどの関係もあることを意味します。

コアクラスにおける同等性比較の実装

クラス eql? == ===
Object 同一かどうか(equal?と同様) eql?と同じ ==と同じ
Symbol - - -
Numeric 同じ型、同じ値 ロケット演算子が0を返す形で値が同じ -
String 同じ長さ、同じ内容 他方がStringならeql?、それ以外はother.to_str===self -
Regexp 他方がRegexpなら同じパターン/同じオプション/同じエンコーディング eql?と同じ 他方がStringならselfと一致
Array 同じ長さかつ全要素が.eql?で互いに等しい 同じ長さかつ全要素が.eql?で互いに等しい: 他方は.to_ary暗黙に変換される -
Hash 同じ長さかつ全要素が==で互いに等しい(順序は問わず) eql?と同じ -
Module - - other.is_a?(self)
Class - - other.is_a?(self)

表の-は未定義またはObjectの実装が使われることを表します。

サブクラスでのベストプラクティス

  • 2つのオブジェクトが同じであると考えられる場合にtrueを返すよう、意味のある==を定義しましょう
  • eql?は、2つのオブジェクトが同一クラスのインスタンスである場合にのみ、==と同じ値を返すようにしましょう
  • equal?は再定義してはなりません
  • ===の実装には創意を盛り込みましょう(ただし納得できるレベルで: 他の人はこれを用いて関係性を便利にチェックできると期待するものです)

参考

  • equalizer -- オブジェクトのインスタンス変数を元に==eql?を自動で定義するgemです

関連記事

Rubyの===演算子についてまとめてみた

Ruby inside: トリプルイコール === の黒魔術(翻訳)


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。