概要
原著者の許諾を得て翻訳・公開いたします。
- 英語記事: Idiosyncratic Ruby: Struggling Four Equality
- 原文更新日: 2017/12/25
- 著者: Jan Lelis
- サイト: Idiosyncratic Ruby
Ruby: 4種類の同等性比較: equal?/eql?/==/===(翻訳)
Rubyのさらなる特異な性質として、同等性の比較があります。さほど複雑ではありませんが、名前に注意しましょう。
同等性比較の4つの概念
1. equal?: オブジェクトの同一性比較
これはわかりやすい概念です。x.object_id == y.object_idのように、2つのオブジェクトが完全に同一であるべきだと考えられます。
2. ==(ダブルイコール): 「同等性」の同等性
これは大抵の場合注意が必要です。2つのオブジェクトは同じとして扱われるべきです。そのクラスで<=>ロケット比較UFO演算子(宇宙船演算子)がサポートされている場合、==が同じ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です