比較 - ruby===== 違い




equal ?, eql ?,===、==の違いは何ですか? (5)

===#---大文字と小文字の区別

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

どちらも同じように動作しますが、 "===" case文

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

ここで違い

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

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

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

今、 eql?が来eql? 。 これは何をするのですか? オペランドのハッシュ/ IDを呼び出しますか?

なぜRubyは非常に多くの平等サインを持っていますか? 彼らはセマンティクスが異なっているはずですか?


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

equalsまたはdouble equalとも呼ばれる==演算子は、両方のオブジェクトが等しい場合はtrueを返し、等しい場合はfalseを返します。

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

!=演算子、AKAの不等式またはbang-tildeは、==の逆です。 両方のオブジェクトが等しくない場合は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を返します。

もう一度やり直してみましょう。この時間だけが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などの一部のプログラミング言語では、ハッシュテーブルと呼ばれるコレクションタイプが用意されています。 ハッシュテーブルは、一意のキーとそれに対応する値で構成されるデータをペアで格納する辞書的なコレクションです。 フードの下では、それらのキーはハッシュコードとして格納されます。 ハッシュテーブルは通常ハッシュテーブルと呼ばれます。 どのように単語ハッシュがハッシュコードまたはハッシュテーブルを参照しているかに注目してください。 Rubyプログラミングの文脈では、ハッシュという単語はほとんど常に辞書のようなコレクションを指します。

Rubyには、ハッシュコードを生成するためのhashという組み込みメソッドが用意されています。 以下の例では、文字列を受け取り、ハッシュコードを返します。 異なるオブジェクトID(異なるオブジェクト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? メソッドは、==メソッドと同様に動作します。 ただし、いくつかの例外があります。 たとえば、eql? 整数と浮動小数点数を比較するときに暗黙の型変換を実行しません。

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

ケース平等演算子:===

String、Range、RegexpなどのRubyの組み込みクラスの多くは、===演算子の独自の実装を提供しています(case-equality、triple equals、またはthreequals)。 それは各クラスで異なって実装されているため、呼び出されたオブジェクトのタイプによって異なる動作をします。 一般に、右側のオブジェクトが左側にあるオブジェクトに属しているか、またはオブジェクトのメンバーになっている場合は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つまり、左側のオペランドのクラスは、===メソッドの実装がどのようになるかを定義しますオペランドの位置は互換性がありません。

Regexpの実装

右側の文字列が左側の正規表現と一致する場合はtrueを返します。 / zen / === "今日の練習"#Output:=> true# "今日の練習" =〜/ 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を返します。

詳細はこのブログの記事をご覧ください。


オブジェクトのドキュメントをここで引用するつもりです。なぜなら、大きな説明があると思うからです。 私はあなたがそれを読むことを奨励し、 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等価

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

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

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

equal? - 同一性比較

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

これは効果的にポインタの比較です。


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

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

ない。

その点を本当に見てみましょう。

JavascriptやPHPの等価演算子としては馴染み深いかもしれませんが、これはRubyの等価演算子ではなく、根本的に異なるセマンティクスを持っています。

それでは何が?

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

  • ===正規表現にマッチする
  • ===範囲メンバーシップのチェック
  • ===チェックはクラスのインスタンスです
  • ===ラムダ式を呼び出す
  • ===時には等価性をチェックしますが、ほとんどは等しくない

では、この狂気はどうやって意味をなすでしょうか?

  • Enumerable#grepは内部的に===使用しEnumerable#grep
  • ステートメントが===内部的に使用されるcase when
  • 楽しい事実、 rescueは内部的に===使用します

そのため、正規表現、クラス、範囲、さらにはラムダ式を使用することができます。

いくつかの例

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でも動作し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]

私は上記のすべてのための簡単なテストを書いた。

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)




equality