web-dev-qa-db-ja.com

Equal?、eql?、===、および==の違いは何ですか?

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

デフォルトでは、=====..を呼び出すequal?を呼び出します。それで、これら3つのメソッドすべてがオーバーライドされないのであれば、=====およびequal?はまったく同じです。

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

なぜRubyはそれほど多くの等号を持っているのですか?それらはセマンティクスが異なるはずですか?

524
denniss

私はそれがいくつかの素晴らしい説明を持っていると思うので、私は重く引用するつもりです オブジェクトドキュメンテーション ここ。 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}

== - 一般的な「平等」

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

これは最も一般的な比較なので、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

case + Regexがコードを非常にきれいにする方法の良い例については、 ここに私の答え を見てください。そしてもちろん、あなた自身の===実装を提供することによって、あなたはカスタムのcaseセマンティクスを得ることができます。

eql? - Hashの同等性

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

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

それであなたはあなた自身の使用のためにこれを自由にオーバーライドすることができます、あるいはあなたは==をオーバーライドしそしてalias :eql? :==を使うことができますので2つのメソッドは同じように振る舞います。

equal? - アイデンティティ比較

==とは異なり、equal?メソッドはサブクラスによってオーバーライドされるべきではありません。つまり、オブジェクトの同一性を判断するために使用されます(つまり、a.equal?(b) iff abと同じオブジェクトである)。

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

755
jtbandes

私は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?を使っています。ハッシュキーを比較する(メソッドhashと組み合わせて)。ほとんどのクラスでは: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クラスセットはハッシュキー比較にも依存しています。

equal?(オブジェクト識別情報の比較)
Rubyは使っている:等しい? 2つのオブジェクトが同一かどうかを確認します。このメソッド(BasicObjectクラスの)は上書きされません。

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

等価演算子:==と!=

==演算子は、等価性または二重等価性とも呼ばれ、両方のオブジェクトが等しい場合は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つの文字列があります。ただし、それらは異なるオブジェクトIDを持つ2つの異なるオブジェクトです。したがって、等しい?メソッドはfalseを返します。

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

a = "zen"
b = a

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

a.equal? b  # Output: => true

eQL?

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

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

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

ハッシュメソッドは、すべてのRubyオブジェクトのデフォルトルートであるObjectクラスに含まれるKernelモジュールに実装されています。 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? methodは==メソッドと同じように動作します。ただし、いくつか例外があります。たとえば、eqlの?整数とfloatを比較するとき、暗黙的に型変換は行われません。

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?メソッドは、Kernelモジュールに実装されています。これは、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/=== "今日のzazen練習"#出力:=> true#は "今日のzazen練習"と同じ= =/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

パターンマッチング演算子:=〜と!〜

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

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文と3項演算子で使用できます。

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を返します。

より詳しい情報は このブログ記事 で入手可能です。

29
BrunoFacca

私は===演算子を拡張したいと思います。

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

違います。

その点を本当によく理解しましょう。

===を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

これらすべての例は、grepメソッドと同様にpattern === valueでも動作します。

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
akuhn

===#---大文字小文字の同一性

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

どちらも似たような働きをしますが、 "==="でもcase文を実行します。

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

ここで違い

String === "test"   #=> true
String == "test"  #=> false
8
Kishore Mohan

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
kalibbala