Rubyにおける==,===,eql?,equal?の違い

どれが何なのかたまに混乱するのでメモ.

==

  • オブジェクトの同値性のチェック
  • クラスやオブジェクトの性質に合わせて再定義できる

===

  • オブジェクトの同値性のチェック(所属性を加味)
  • case式で使われる
  • クラスやオブジェクトの性質に合わせて再定義できる

eql?

  • オブジェクトの同値性のチェック(==より厳密)
  • Hashで二つのキーが等しいかどうかを判定するのに使われる
  • クラスやオブジェクトの性質に合わせて再定義できる
  • 再定義した場合はhashメソッドも再定義しなければならない

equal?

  • オブジェクトの同一性のチェック
  • object_idが一致するかどうかを調べる
  • 再定義してはならない

比較表

効果使用場面再定義
==同値性のチェック同値性のチェック
===同値性のチェック(所属性)case式
eql?同値性のチェック(厳密)Hashキーの比較○(※)
equal?同一性のチェック同一性のチェック×
※ eql?を再定義した場合はhashメソッドも再定義しなければならない.

Numeric#==,Numeric#===は両辺が数値的に等しければtrue.Numeric#eql?は両辺が数値的に等しくてもクラスが同じでなければfalse.

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

3.14 == 3.14000    #=> true
3.14 === 3.14000   #=> true
3.14.eql?(3.14000) #=> true

Regexp#===は右辺のStringが左辺の正規表現にマッチすればtrue.

/foo/ == "foo"  #=> false
/foo/ === "foo" #=> true
/foo/ === "bar" #=> false
"foo" === /foo/ #=> false  # String#===が呼び出されている

Range#===は右辺のオブジェクトが左辺に含まれるならばtrue.

(0..10) == 1  #=> false
(0..10) === 1 #=> true
1 === (0..10) #=> false  # Fixnum#===が呼び出されている

Module#===,Class#===は右辺のオブジェクトが左辺のクラスかそのサブクラスのインスタンスならばtrue.

Numeric === 1.0 #=> true
Integer === 1.0 #=> false

equal?は両辺のオブジェクトのobject_idが等しければtrue.Symbolは同じ内容ならば必ず同一object_idになる.

a = b = "foo"
a.equal?(b) #=> true

a = "bar"
b = "bar"
a.equal?(b) #=> false

a = :baz
b = :baz
a.equal?(b) #=> true

以上

基本的に==を使っていれば事足りそうではあるが,===についてはクラスごとの性質を知っておくとcase式の書き方の幅が広がるので知っておく価値はありそう.

n = 10 ** 100

# nは === の右辺になる
type = case n
when Fixnum
  "int"
when Bignum
  "longer than int"
when Float
  "double"
else
  "something else"
end

puts type #=> longer than int