equal?、eql?、===、==の違いは何ですか?


552

これら4つの方法の違いを理解しようとしています。デフォルトで==は、equal?両方のオペランドがまったく同じオブジェクトを参照している場合にtrueを返すメソッドを呼び出します。

===デフォルトでも通話==呼び出しがequal?...大丈夫なので、すべてのこれらの三つの方法がオーバーライドされていない場合は、その後、私は推測する =====equal?まったく同じことを行いますか?

今来るeql?。これは何をしますか(デフォルト)?オペランドのハッシュ/ IDを呼び出しますか?

Rubyにはなぜ多くの等号があるのですか?それらはセマンティクスが異なるはずですか?


私はちょうどIRBを開始し、あなたに反する次の結果を持っていた...これらの3のすべてが真である:"a" == "a""a" === "a""a".eql? "a"。しかし、これは誤りです"a".equal? "a"(私のものはルビー1.9.2-p180です)
PeterWong

7
@ピーター:文字列はすべての等価演算子をオーバーライドするためです。使用しようとするとa = Object.new; b = Object.new、すべてのその後=====.equal?.eql?返されますtrueためにaaしてのために偽のab
Nemo157

回答:


785

ここにはオブジェクトのドキュメントを大きく引用ます。これにはいくつかの素晴らしい説明があると思います。それを読むことをお勧めします。また、Stringなどの他のクラスでオーバーライドされるため、これらのメソッドのドキュメントも読んでください。

補足:別のオブジェクトでこれらを自分で試してみたい場合は、次のようなものを使用します。

class Object
  def all_equals(o)
    ops = [:==, :===, :eql?, :equal?]
    Hash[ops.map(&:to_s).zip(ops.map {|s| send(s, o) })]
  end
end

"a".all_equals "a" # => {"=="=>true, "==="=>true, "eql?"=>true, "equal?"=>false}

== —一般的な「平等」

オブジェクトレベルで、==場合にのみtrueを返すobjother同じオブジェクトです。通常、このメソッドは下位クラスでオーバーライドされ、クラス固有の意味を提供します。

これは最も一般的な比較であり、したがって(クラスの作成者として)2つのオブジェクトが「等しい」かどうかを判断する最も基本的な場所です。

=== —大文字と小文字の区別

クラスObjectの場合、を呼び出すのと実質的に同じです#==が、通常は子孫によってオーバーライドされ、caseステートメントで意味のあるセマンティクスを提供します。

これは非常に便利です。興味深い===実装の例:

  • 範囲
  • 正規表現
  • Proc(Ruby 1.9の場合)

したがって、次のようなことができます。

case some_object
when /a regex/
  # The regex matches
when 2..4
  # some_object is in the range 2..4
when lambda {|x| some_crazy_custom_predicate }
  # the lambda returned true
end

+ がコードをよりクリーンにする方法のきちんとした例については、ここ私の回答を参照しください。そしてもちろん、独自の実装を提供することで、カスタムのセマンティクスを取得できます。caseRegex===case

eql?Hash平等

eql?場合、このメソッドはtrueを返しますobjし、other同じハッシュキーを参照してください。これは、Hashメンバーが等しいかどうかをテストするために使用されます。クラスのオブジェクトの場合Objecteql?と同義です==サブクラスは通常eql?、オーバーライドされた==メソッドへのエイリアスによってこの伝統を継続しますが、例外があります。Numericたとえば、型は全体==で型変換を実行しますが、全体では実行しないeql?ため、次のようになります。

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

したがって、これを独自に使用==するalias :eql? :==ためにオーバーライドするか、2つのメソッドが同じように動作するようにオーバーライドして使用することができます。

equal? —アイデンティティの比較

とは異なり==、このequal?メソッドをサブクラスでオーバーライドすることはできません。このメソッドは、オブジェクトの識別に使用されます(つまり、a.equal?(b)iffはとa同じオブジェクトですb)。

これは実質的にポインター比較です。


32
あなたの答えから私が理解するように、厳密さは次のとおりです。<eql?<== <===。通常は、==を使用します。ゆるい目的の場合は、===を使用します。厳密な状況では、eql?を使用し、完全なIDには、equal?を使用します。
sawa

21
厳格性の概念は強制されておらず、ドキュメントでも示唆されていません。Numericそれは、より厳密にそれを処理する場合にのみ発生します==。それはクラスの作者次第です。ステートメントの===外ではほとんど使用されませんcase
jtbandes 2012年

4
==は、大きい/小さいという点でも同等です。つまり、Comparableを含めると、<=>が0を返すという意味で定義されます。これが、1 == 1.0がtrueを返す理由です。
apeiros

5
@sawa私は通常、===(大体)「一致する」という意味だと思います。のように、「正規表現は文字列と一致します」または「範囲一致は(数値を含む)一致します」。
ケルビン

7
面白い事実:公式ドキュメントがこの回答にリンクしています(ruby-doc.org/core-2.1.5/…を参照)。
Mark Amery 14

46

私はjtbandesの回答が大好きですが、かなり長いので、独自のコンパクトな回答を追加します。

=====eql?equal?
4個のコンパレータ、すなわちです。Rubyで2つのオブジェクトを比較する4つの方法。
Rubyでは、すべてのコンパレーター(およびほとんどの演算子)は実際にはメソッド呼び出しであるため、これらの比較メソッドのセマンティクスを自分で変更、上書き、および定義できます。ただし、Rubyの内部言語構造がどのコンパレーターを使用するかを理解することが重要です。

==(値の比較)
Rubyは:==をどこでも使用して、2つのオブジェクトのを比較します。ハッシュ値:

{a: 'z'}  ==  {a: 'Z'}    # => false
{a: 1}    ==  {a: 1.0}    # => true

===(大文字と小文字の比較)
Rubyは:/ ==をcase / when構文で使用します。次のコードスニペットは論理的に同じです。

case foo
  when bar;  p 'do something'
end

if bar === foo
  p 'do something'
end

eql?(ハッシュキーの比較)
Rubyは:eql?(メソッドハッシュと組み合わせて)ハッシュキーを比較します。ほとんどのクラスでは:eql?:==と同じです。
:eqlに関する知識 独自の特別なクラスを作成する場合にのみ重要です。

class Equ
  attr_accessor :val
  alias_method  :initialize, :val=
  def hash()           self.val % 2             end
  def eql?(other)      self.hash == other.hash  end
end

h = {Equ.new(3) => 3,  Equ.new(8) => 8,  Equ.new(15) => 15}    #3 entries, but 2 are :eql?
h.size            # => 2
h[Equ.new(27)]    # => 15

注:一般的に使用されるRubyクラスセットもHash-key-comparisonに依存しています。

equal?(オブジェクトIDの比較)
Rubyでは:equalを使用していますか?2つのオブジェクトが同一かどうかを確認します。このメソッド(クラスBasicObjectの)は上書きされることになっています。

obj = obj2 = 'a'
obj.equal? obj2       # => true
obj.equal? obj.dup    # => false

30
良い答えですが、jtbandesとほぼ同じです。:)
オディティー

2
@odigity、約70%の長さ。その30%を使うために多くのことを考えることができました。
Cary Swoveland、2018年

の例eql?は非常に誤解を招くと思います。ハッシュの計算方法と一致eql?する等価比較です。つまり、が保証されます。単にハッシュコードを比較するだけではありませa.eql?(b)a.hash == b.hash
Andrey Tarantsov

である場合の比較が本当に同等でbar === fooはありませんかfoo === bar?後者が正しいことを願っています。コンパイラが左側を呼び出すので重要です:=== `'
Alexis Wilke

私の知る限り、bar === fooRubyは左側のケース値と右側のケース変数を使用します。これは、NPE(ヌルポインター例外)の回避に関係している可能性があります。
Andreas Rayo Kniep

34

等価演算子:==および!=

==演算子は、等価または二重等価とも呼ばれ、両方のオブジェクトが等しい場合はtrueを返し、等しくない場合はfalseを返します。

"koan" == "koan" # Output: => true

!=演算子は不等式とも呼ばれ、==の反対です。両方のオブジェクトが等しくない場合はtrueを返し、等しい場合はfalseを返します。

"koan" != "discursive thought" # Output: => true

異なる順序で同じ要素を持つ2つの配列は等しくない、同じ文字の大文字と小文字のバージョンは等しくない、などのことに注意してください。

異なる型(整数と浮動小数点など)の数を比較するときに、それらの数値が同じ場合、==はtrueを返します。

2 == 2.0 # Output: => true

等しい?

両方のオペランドが等しいかどうかをテストする==演算子とは異なり、equalメソッドは2つのオペランドが同じオブジェクトを参照しているかどうかをチェックします。これは、Rubyでの最も厳密な等価形式です。

例:a = "zen" b = "zen"

a.object_id  # Output: => 20139460
b.object_id  # Output :=> 19972120

a.equal? b  # Output: => false

上記の例では、同じ値の2つの文字列があります。ただし、これらは2つの異なるオブジェクトであり、オブジェクトIDが異なります。したがって、等しいですか?メソッドはfalseを返します。

もう一度試してみましょう。今回はbだけがaへの参照になります。両方の変数が同じオブジェクトを指しているため、オブジェクトIDは両方の変数で同じであることに注意してください。

a = "zen"
b = a

a.object_id  # Output: => 18637360
b.object_id  # Output: => 18637360

a.equal? b  # Output: => true

eql?

ハッシュクラスでは、eql?メソッドがキーの等価性をテストするために使用されます。これを説明するにはいくつかの背景が必要です。計算の一般的なコンテキストでは、ハッシュ関数は任意のサイズの文字列(またはファイル)を受け取り、ハッシュコードと呼ばれる固定サイズの文字列または整数を生成します。一般的に使用されるハッシュコードタイプには、MD5、SHA-1、CRCなどがあります。これらは、暗号化アルゴリズム、データベースインデックス、ファイル整合性チェックなどで使用されます。Rubyなどの一部のプログラミング言語は、ハッシュテーブルと呼ばれるコレクション型を提供します。ハッシュテーブルは、データをペアで格納する辞書のようなコレクションであり、一意のキーとそれに対応する値で構成されます。内部では、これらのキーはハッシュコードとして保存されます。ハッシュテーブルは通常、単にハッシュと呼ばれます。hashという単語がハッシュコードまたはハッシュテーブルを参照する方法に注意してください。

Rubyには、ハッシュコードを生成するためのハッシュと呼ばれる組み込みメソッドが用意されています。以下の例では、文字列を受け取り、ハッシュコードを返します。異なるオブジェクト(異なるオブジェクトIDを持つ)であっても、同じ値の文字列は常に同じハッシュコードを持っていることに注意してください。

"meditation".hash  # Output: => 1396080688894079547
"meditation".hash  # Output: => 1396080688894079547
"meditation".hash  # Output: => 1396080688894079547

ハッシュメソッドは、すべてのRubyオブジェクトのデフォルトルートであるObjectクラスに含まれるカーネルモジュールに実装されています。SymbolやIntegerなどの一部のクラスはデフォルトの実装を使用し、StringやHashなどのクラスは独自の実装を提供します。

Symbol.instance_method(:hash).owner  # Output: => Kernel
Integer.instance_method(:hash).owner # Output: => Kernel

String.instance_method(:hash).owner  # Output: => String
Hash.instance_method(:hash).owner  # Output: => Hash

Rubyでは、ハッシュ(コレクション)に何かを格納すると、キーとして提供されたオブジェクト(文字列やシンボルなど)がハッシュコードに変換されて格納されます。後で、ハッシュ(コレクション)から要素を取得するときに、オブジェクトをキーとして提供します。オブジェクトはハッシュコードに変換され、既存のキーと比較されます。一致する場合、対応するアイテムの値が返されます。比較はeql?フードの下のメソッド。

"zen".eql? "zen"    # Output: => true
# is the same as
"zen".hash == "zen".hash # Output: => true

ほとんどの場合、eql?メソッドの動作は==メソッドと同じです。ただし、いくつかの例外があります。たとえば、eql?整数を浮動小数点数と比較するときに暗黙の型変換を実行しません。

2 == 2.0    # Output: => true
2.eql? 2.0    # Output: => false
2.hash == 2.0.hash  # Output: => false

大文字と小文字の等価演算子:===

String、Range、RegexpなどのRubyの組み込みクラスの多くは、===演算子の独自の実装を提供します。クラスごとに実装が異なるため、呼び出されたオブジェクトのタイプによって動作が異なります。通常、右側のオブジェクトが左側のオブジェクトに「属している」または「のメンバーである」場合、trueを返します。たとえば、オブジェクトがクラス(またはそのサブクラスの1つ)のインスタンスであるかどうかをテストするために使用できます。

String === "zen"  # Output: => true
Range === (1..2)   # Output: => true
Array === [1,2,3]   # Output: => true
Integer === 2   # Output: => true

同じ結果は、おそらくその仕事に最適な他の方法でも達成できます。通常は、効率と簡潔さを犠牲にすることなく、できるだけ明示的にすることで読みやすいコードを作成することをお勧めします。

2.is_a? Integer   # Output: => true
2.kind_of? Integer  # Output: => true
2.instance_of? Integer # Output: => false

2などの整数はIntegerクラスのサブクラスであるFixnumクラスのインスタンスであるため、最後の例はfalseを返しました。===、is_a?およびinstance_of?オブジェクトが指定されたクラスまたは任意のサブクラスのインスタンスである場合、メソッドはtrueを返します。instance_ofメソッドはより厳密で、オブジェクトがサブクラスではなく、その正確なクラスのインスタンスである場合にのみtrueを返します。

is_a?そして、kind_of?メソッドは、Objectクラスによって混合されるカーネルモジュールに実装されます。どちらも同じメソッドのエイリアスです。確認しましょう:

Kernel.instance_method(:kind_of?)== Kernel.instance_method(:is_a?)#出力:=> true

===の範囲の実装

===演算子が範囲オブジェクトで呼び出されると、右側の値が左側の範囲内にある場合にtrueを返します。

(1..4) === 3  # Output: => true
(1..4) === 2.345 # Output: => true
(1..4) === 6  # Output: => false

("a".."d") === "c" # Output: => true
("a".."d") === "e" # Output: => false

===演算子は、左側のオブジェクトの===メソッドを呼び出すことに注意してください。したがって、(1..4)=== 3は(1..4)。=== 3と同等です。つまり、左側のオペランドのクラスは、===メソッドの実装を定義します呼び出されるため、オペランドの位置は交換できません。

===の正規表現の実装

右側の文字列が左側の正規表現と一致する場合はtrueを返します。/ zen / === "practice zazen today"#出力:=> true#は "practice zazen today" =〜/ zen /と同じです

case / whenステートメントでの===演算子の暗黙的な使用

この演算子は、case / whenステートメントの内部でも使用されます。それが最も一般的な用途です。

minutes = 15

case minutes
  when 10..20
    puts "match"
  else
    puts "no match"
end

# Output: match

上記の例で、Rubyが暗黙的に二重等号演算子(==)を使用した場合、範囲10..20は15などの整数と等しいとは見なされません。三重等号演算子(===)はすべてのcase / whenステートメントで暗黙的に使用されます。上記の例のコードは次と同等です。

if (10..20) === minutes
  puts "match"
else
  puts "no match"
end

パターンマッチング演算子:=〜および!〜

=〜(等しいチルド)および!〜(バング-チルド)演算子は、文字列とシンボルを正規表現パターンと照合するために使用されます。

StringクラスとSymbolクラスの=〜メソッドの実装では、引数として正規表現(Regexpクラスのインスタンス)が必要です。

"practice zazen" =~ /zen/   # Output: => 11
"practice zazen" =~ /discursive thought/ # Output: => nil

:zazen =~ /zen/    # Output: => 2
:zazen =~ /discursive thought/  # Output: => nil

Regexpクラスの実装では、引数として文字列またはシンボルが必要です。

/zen/ =~ "practice zazen"  # Output: => 11
/zen/ =~ "discursive thought" # Output: => nil

すべての実装で、文字列またはシンボルがRegexpパターンに一致すると、一致の位置(インデックス)である整数を返します。一致しない場合は、nilを返します。Rubyでは、整数値はすべて「truthy」であり、nilは「falsy」であるため、=〜演算子はifステートメントや三項演算子で使用できます。

puts "yes" if "zazen" =~ /zen/ # Output: => yes
"zazen" =~ /zen/?"yes":"no" # Output: => yes

パターンマッチング演算子は、短いifステートメントを記述する場合にも役立ちます。例:

if meditation_type == "zazen" || meditation_type == "shikantaza" || meditation_type == "kinhin"
  true
end
Can be rewritten as:
if meditation_type =~ /^(zazen|shikantaza|kinhin)$/
  true
end

!〜演算子は=〜の逆であり、一致がない場合はtrueを返し、一致する場合はfalseを返します。

詳細については、このブログ投稿をご覧ください。


6
これは現在受け入れられている答えよりも良い答えだと思います。良い例が提供されており、さまざまな種類の等式が何を意味するのか、なぜそれらが存在するのか/どこで使用されているのかについて曖昧ではありません。
Qqwy 2016年

1
非常に詳細な回答ですが、私のirb(ruby v 2.2.1)では:zen === "zen"falseが返されます
Mike R

@MikeRお知らせいただきありがとうございます。答えを修正しました。
BrunoFacca

私はtype_ofを意味すると思いますか?「2などの整数はIntegerクラスのサブクラスであるFixnumクラスのインスタンスであるため、最後の例ではfalseが返されることに注意してください。===、is_a?、instance_of?(TYPE_OF?)」?
user1883793

1
私はこの答えが大好きです。ありがとう
アブドラファデル2017

9

Rubyは、同等性を処理するためのいくつかの異なるメソッドを公開しています。

a.equal?(b) # object identity - a and b refer to the same object

a.eql?(b) # object equivalence - a and b have the same value

a == b # object equivalence - a and b have the same value with type conversion.

以下のリンクをクリックして読み続けてください。これにより、明確に要約された理解が得られました。

https://www.relishapp.com/rspec/rspec-expectations/v/2-0/docs/matchers/equality-matchers

それが他の人を助けることを願っています。


8

===#---ケースの同等性

==#---一般的な平等

どちらも同様に機能しますが、 "==="でcaseステートメントを実行することもできます

"test" == "test"  #=> true
"test" === "test" #=> true

ここの違い

String === "test"   #=> true
String == "test"  #=> false

3
彼らはありません、それはときに真となる傾向にあるにもかかわらず、同じように動作しa==b、その後a===b。しかしa===b、はるかに強力です。 ===対称ではない、とa===bは大きく異なることを意味しb===aおろかが、a==b
mwfearnley

8

拡大したい ===オペレーター詳しく説明します。

=== 等価演算子ではありません!

ない。

そのポイントを実際に渡ってみましょう。

===JavascriptとPHPの等値演算子として慣れているかもしれませんが、これはRubyの等値演算子ではなく、根本的にセマンティクスが異なります。

それで、何をし===ますか?

=== パターンマッチング演算子です!

  • === 正規表現に一致
  • === 範囲のメンバーシップをチェックします
  • === クラスのインスタンスであることを確認します
  • === ラムダ式を呼び出します
  • === 時々等式をチェックしますが、ほとんどの場合

では、この狂気はどのように意味をなすのでしょうか?

  • Enumerable#grep===内部で使用
  • case whenステートメントは===内部で使用します
  • 楽しい事実、内部でrescue使用===

これが、case whenステートメントで正規表現、クラス、範囲、さらにはラムダ式を使用できる理由です。

いくつかの例

case value
when /regexp/
  # value matches this regexp
when 4..10
  # value is in range
when MyClass
  # value is an instance of class
when ->(value) { ... }
  # lambda expression returns true
when a, b, c, d
  # value matches one of a through d with `===`
when *array
  # value matches an element in array with `===`
when x
  # values is equal to x unless x is one of the above
end

これらの例はすべてpattern === valuegrepメソッドとともに機能します。

arr = ['the', 'quick', 'brown', 'fox', 1, 1, 2, 3, 5, 8, 13]
arr.grep(/[qx]/)                                                                                                                            
# => ["quick", "fox"]
arr.grep(4..10)
# => [5, 8]
arr.grep(String)
# => ["the", "quick", "brown", "fox"]
arr.grep(1)
# => [1, 1]

-8

上記すべての簡単なテストを作成しました。

def eq(a, b)
  puts "#{[a, '==',  b]} : #{a == b}"
  puts "#{[a, '===', b]} : #{a === b}"
  puts "#{[a, '.eql?', b]} : #{a.eql?(b)}"
  puts "#{[a, '.equal?', b]} : #{a.equal?(b)}"
end

eq("all", "all")
eq(:all, :all)
eq(Object.new, Object.new)
eq(3, 3)
eq(1, 1.0)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.