Question Quelle est la différence entre égal?, Eql?, === et ==?


J'essaie de comprendre la différence entre ces quatre méthodes. Je sais par défaut que == appelle la méthode equal? qui renvoie true lorsque les deux opérandes se réfèrent exactement au même objet.

=== par défaut aussi appels == qui appelle equal?... okay, donc si toutes ces trois méthodes ne sont pas remplacées, alors je suppose ===, == et equal? faire exactement la même chose?

Maintenant vient eql?. Qu'est-ce que cela fait (par défaut)? Fait-il un appel au hash / id de l'opérande?

Pourquoi Ruby a-t-elle autant de signes d'égalité? Sont-ils censés différer en sémantique?


483
2017-08-23 06:13


origine


Réponses:


Je vais fortement citer la documentation d'objet ici, parce que je pense qu'il a de bonnes explications. Je vous encourage à le lire, ainsi que la documentation pour ces méthodes car elles sont remplacées dans d'autres classes, comme Chaîne.

Note latérale: si vous voulez essayer par vous-même sur différents objets, utilisez quelque chose comme ceci:

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}

== - "égalité" générique

Au niveau de l'objet, == ne retourne vrai que si obj et other sont le même objet. En règle générale, cette méthode est substituée dans les classes descendantes pour fournir une signification spécifique à la classe.

C'est la comparaison la plus courante, et donc l'endroit le plus fondamental où vous (en tant qu'auteur d'une classe) décidez si deux objets sont "égaux" ou non.

=== - égalité de cas

Pour la classe Object, effectivement identique à l'appel #==, mais généralement remplacée par les descendants pour fournir une sémantique significative dans les instructions de cas.

Ceci est incroyablement utile. Des exemples de choses intéressantes === implémentations:

  • Gamme
  • Regex
  • Proc (en Ruby 1.9)

Donc, vous pouvez faire des choses comme:

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

Voir ma réponse ici pour un bel exemple de comment case+Regex peut rendre le code beaucoup plus propre. Et bien sûr, en fournissant votre propre === mise en œuvre, vous pouvez obtenir personnalisé case sémantique.

eql? - Hash égalité

le eql? la méthode retourne vrai si obj et other se référer à la même clé de hachage. Ceci est utilisé par Hash pour tester les membres pour l'égalité. Pour les objets de classe Object, eql? est synonyme de ==. Les sous-classes continuent normalement cette tradition par aliasing eql? à leur dépassé == méthode, mais il y a des exceptions. Numeric types, par exemple, effectuer la conversion de type à travers ==, mais pas à travers eql?, alors:

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

Vous êtes donc libre de remplacer cela pour vos propres utilisations, ou vous pouvez remplacer == et utilise alias :eql? :== les deux méthodes se comportent donc de la même manière.

equal? - comparaison d'identité

contrairement à ==, la equal? méthode ne doit jamais être remplacée par des sous-classes: elle est utilisée pour déterminer l’identité de l’objet a.equal?(b) si a est le même objet que b).

C'est effectivement une comparaison de pointeur.


705
2017-08-23 06:25



J'adore jtbandes répondre, mais comme c'est assez long, j'ajouterai ma propre réponse compacte:

==, ===, eql?, equal?
sont 4 comparateurs, c.-à-d. 4 façons de comparer 2 objets, en Ruby.
Comme, dans Ruby, tous les comparateurs (et la plupart des opérateurs) sont en fait des appels de méthode, vous pouvez modifier, écraser et définir vous-même la sémantique de ces méthodes de comparaison. Cependant, il est important de comprendre, lorsque les constructions de langage interne de Ruby utilisent quel comparateur:

== (comparaison de valeur)
Ruby utilise: == partout pour comparer le valeurs de 2 objets, par ex. Valeurs de hachage:

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

=== (comparaison de cas)
Ruby utilise: === dans le cas / quand construit. Les extraits de code suivants sont logiquement identiques:

case foo
  when bar;  p 'do something'
end

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

eql? (Comparaison clé-hachage)
Ruby utilise: eql? (en combinaison avec le hachage de la méthode) pour comparer les clés Hash. Dans la plupart des classes: eql? est identique à: ==.
Connaissance de: eql? est seulement important, quand vous voulez créer vos propres classes spéciales:

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

Remarque: L'ensemble Ruby-class couramment utilisé repose également sur la comparaison des clés de hachage.

equal? (comparaison d'identité d'objet)
Ruby utilise: égal? pour vérifier si deux objets sont identiques. Cette méthode (de classe BasicObject) n'est pas censée être écrasée.

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

37
2018-04-06 00:42



Opérateurs d'égalité: == et! =

L'opérateur ==, également connu sous le nom d'égalité ou de double égalité, retournera vrai si les deux objets sont égaux et faux s'ils ne le sont pas.

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

L'opérateur! =, AKA inégalité ou bang-tilde, est l'opposé de ==. Il retournera true si les deux objets ne sont pas égaux et false s'ils sont égaux.

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

Notez que deux tableaux avec les mêmes éléments dans un ordre différent ne sont pas égaux, les versions majuscules et minuscules de la même lettre ne sont pas égales et ainsi de suite.

Lorsque vous comparez des nombres de types différents (par exemple, entier et flottant), si leur valeur numérique est la même, == retournera vrai.

2 == 2.0 # Output: => true

égal?

Contrairement à l'opérateur == qui teste si les deux opérandes sont égaux, la méthode equal vérifie si les deux opérandes se réfèrent au même objet. C'est la forme la plus stricte de l'égalité chez Ruby.

Exemple:     a = "zen"     b = "zen"

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

a.equal? b  # Output: => false

Dans l'exemple ci-dessus, nous avons deux chaînes avec la même valeur. Cependant, ils sont deux objets distincts, avec différents ID d'objet. D'où l'égalité? méthode retournera faux.

Essayons encore, seulement cette fois b sera une référence à un. Notez que l'ID d'objet est le même pour les deux variables, car elles pointent vers le même objet.

a = "zen"
b = a

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

a.equal? b  # Output: => true

eql?

Dans la classe Hash, l'eql? méthode, il est utilisé pour tester les clés pour l'égalité. Quelques informations sont nécessaires pour expliquer cela. Dans le contexte général de l'informatique, une fonction de hachage prend une chaîne (ou un fichier) de n'importe quelle taille et génère une chaîne ou un entier de taille fixe appelé hashcode, généralement appelé hachage uniquement. Certains types de hashcode couramment utilisés sont MD5, SHA-1 et CRC. Ils sont utilisés dans les algorithmes de chiffrement, l'indexation des bases de données, la vérification de l'intégrité des fichiers, etc. Certains langages de programmation, tels que Ruby, fournissent un type de collection appelé table de hachage. Les tables de hachage sont des collections de type dictionnaire qui stockent les données par paires, constituées de clés uniques et de leurs valeurs correspondantes. Sous le capot, ces clés sont stockées sous forme de hashcodes. Les tables de hachage sont communément appelées «hachages». Remarquez comment le mot hash peut faire référence à un hashcode ou à une table de hachage. Dans le contexte de la programmation Ruby, le mot hash fait presque toujours référence à la collection de type dictionnaire.

Ruby fournit une méthode intégrée appelée hash pour générer des hashcodes. Dans l'exemple ci-dessous, il prend une chaîne et retourne un hashcode. Notez que les chaînes ayant la même valeur ont toujours le même code, même s'il s'agit d'objets distincts (avec des ID d'objet différents).

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

La méthode de hachage est implémentée dans le module Kernel, inclus dans la classe Object, qui est la racine par défaut de tous les objets Ruby. Certaines classes telles que Symbol et Integer utilisent l'implémentation par défaut, d'autres comme String et Hash fournissent leurs propres implémentations.

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

Dans Ruby, lorsque nous stockons quelque chose dans un hachage (collection), l'objet fourni en tant que clé (par exemple, une chaîne ou un symbole) est converti et stocké en tant que code de hachage. Plus tard, lors de la récupération d'un élément du hachage (collection), nous fournissons un objet en tant que clé, qui est converti en code de hachage et comparé aux clés existantes. S'il y a correspondance, la valeur de l'élément correspondant est renvoyée. La comparaison est faite en utilisant l'eql? méthode sous le capot.

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

Dans la plupart des cas, l'eql? La méthode se comporte comme la méthode ==. Cependant, il y a quelques exceptions. Par exemple, eql? n'effectue pas de conversion de type implicite lors de la comparaison d'un entier à un flottant.

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

Opérateur d'égalité de cas: ===

 

Beaucoup de classes intégrées de Ruby, telles que String, Range et Regexp, fournissent leurs propres implémentations de l'opérateur ===, également connu sous le nom d'égalité des cas, triple égal ou troisquals. Comme il est implémenté différemment dans chaque classe, il se comportera différemment selon le type d'objet sur lequel il a été appelé. Généralement, il renvoie true si l'objet à droite "appartient à" ou "est membre de" l'objet à gauche. Par exemple, il peut être utilisé pour tester si un objet est une instance d'une classe (ou de l'une de ses sous-classes).

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

Le même résultat peut être obtenu avec d'autres méthodes qui sont probablement les mieux adaptées au travail. Il est généralement préférable d'écrire du code facile à lire en étant aussi explicite que possible, sans sacrifier l'efficacité et la concision.

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

Notez que le dernier exemple a renvoyé false car les entiers tels que 2 sont des instances de la classe Fixnum, qui est une sous-classe de la classe Integer. Le ===, is_a? et instance_of? Les méthodes retournent true si l'objet est une instance de la classe donnée ou de toute sous-classe. La méthode instance_of est plus stricte et renvoie uniquement la valeur true si l'objet est une instance de cette classe exacte et non une sous-classe.

Le is_a? et kind_of? Les méthodes sont implémentées dans le module Kernel, qui est mélangé par la classe Object. Les deux sont des alias de la même méthode. Vérifions:

Kernel.instance_method (: kind_of?) == Kernel.instance_method (: is_a?) # Résultat: => true

Implémentation de la plage de ===

Lorsque l'opérateur === est appelé sur un objet range, il renvoie true si la valeur sur la droite se situe dans la plage de gauche.

(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

Souvenez-vous que l'opérateur === invoque la méthode === de l'objet de gauche. Donc (1..4) === 3 est équivalent à (1..4). === 3. En d'autres termes, la classe de l'opérande de gauche définira quelle implémentation de la méthode === sera appelé, donc les positions des opérandes ne sont pas interchangeables.

Implémentation de Regexp de ===

Renvoie true si la chaîne de droite correspond à l'expression régulière sur la gauche.     / zen / === "pratique zazen aujourd'hui" # Sortie: => true     # est le même que     "pratique zazen aujourd'hui" = ~ / zen /

Utilisation implicite de l'opérateur === sur les requêtes case / when

Cet opérateur est également utilisé sous le capot sur les déclarations cas / when. C'est son utilisation la plus courante.

minutes = 15

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

# Output: match

Dans l'exemple ci-dessus, si Ruby avait implicitement utilisé l'opérateur double égal (==), l'intervalle 10..20 ne serait pas considéré comme égal à un entier tel que 15. Ils correspondent parce que l'opérateur triple égal (===) est implicitement utilisé dans toutes les instructions case / when. Le code dans l'exemple ci-dessus est équivalent à:

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

Opérateurs de correspondance de modèle: = ~ et! ~

 

Les opérateurs = ~ (égal-tilde) et! ~ (Bang-tilde) sont utilisés pour faire correspondre les chaînes et les symboles avec les modèles regex.

L'implémentation de la méthode = ~ dans les classes String et Symbol attend une expression régulière (une instance de la classe Regexp) en tant qu'argument.

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

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

L'implémentation dans la classe Regexp attend une chaîne ou un symbole en tant qu'argument.

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

Dans toutes les implémentations, lorsque la chaîne ou le symbole correspond au modèle Regexp, il renvoie un entier qui est la position (index) de la correspondance. S'il n'y a pas de correspondance, il retourne zéro. Rappelez-vous que dans Ruby, toute valeur entière est "truthy" et nil est "falsy", donc l'opérateur = ~ peut être utilisé dans les instructions if et les opérateurs ternaires.

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

Les opérateurs de correspondance de modèles sont également utiles pour écrire des instructions if plus courtes. Exemple:

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

L'opérateur! ~ Est l'opposé de = ~, il retourne true lorsqu'il n'y a pas de correspondance et false s'il y a une correspondance.

Plus d'infos est disponible à ce blog.


22
2018-05-29 16:47



=== # --- égalité des cas

== # --- égalité générique

les deux fonctionnent de manière similaire mais "===" fait même des déclarations de cas

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

ici la différence

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

7
2018-04-22 09:13



Ruby expose plusieurs méthodes différentes pour gérer l'égalité:

a.equal? ​​(b) # identité d'objet - a et b se réfèrent au même objet

a.eql? (b) # équivalence d'objet - a et b ont la même valeur

a == b # équivalence d'objet - a et b ont la même valeur avec conversion de type.

Continuer à lire en cliquant sur le lien ci-dessous, cela m'a donné une compréhension claire et synthétique.

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

J'espère que ça aide les autres.


6
2018-04-11 20:43



Je voudrais développer sur le === opérateur.

=== n'est pas un opérateur d'égalité!

Ne pas.

Voyons ce point vraiment à travers.

Vous connaissez peut-être === en tant qu'opérateur d'égalité dans Javascript et PHP, mais ce n'est tout simplement pas un opérateur d'égalité dans Ruby et a une sémantique fondamentalement différente.

Alors qu'est-ce que === faire?

=== est l'opérateur de correspondance de modèle!

  • === correspond aux expressions régulières
  • === Adhésion à la gamme de chèques
  • === vérifie l'instance d'une classe
  • === appelle les expressions lambda
  • === parfois vérifie l'égalité, mais surtout il ne

Alors, comment cette folie a-t-elle un sens?

  • Enumerable#grep les usages === intérieurement
  • case when les déclarations utilisent === intérieurement
  • Fait amusant, rescue les usages === intérieurement

C'est pourquoi vous pouvez utiliser des expressions régulières et des classes et des plages et même des expressions lambda dans un case when déclaration.

Quelques exemples

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

Tous ces exemples travaillent avec pattern === value aussi bien qu'avec grep méthode.

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]

4
2018-03-12 02:02



J'ai écrit un test simple pour tout ce qui précède.

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)

-8
2018-04-25 23:02