Recherche…


La différence entre EQ et EQL

  1. EQ vérifie si deux valeurs ont la même adresse de mémoire: en d'autres termes, il vérifie si les deux valeurs sont en fait le même objet identique . Donc, il peut être considéré comme le test d'identité, et ne devrait être appliqué qu'aux structures: conses, tableaux, structures, objets, généralement pour voir si vous traitez avec le même objet "atteint" par différents chemins différentes variables.

  2. EQL vérifie si deux structures sont le même objet (comme EQ ) ou si elles sont les mêmes valeurs non structurées (c'est-à-dire les mêmes valeurs numériques pour les nombres du même type ou les valeurs de caractères). Comme il inclut l'opérateur EQ et peut être utilisé également sur des valeurs non structurées, il s'agit de l'opérateur le plus important et le plus utilisé, et presque toutes les fonctions primitives nécessitant une comparaison d'égalité, comme MEMBER , utilisent par défaut cet opérateur .

Donc, il est toujours vrai que (EQ XY) implique (EQL XY) , tandis que le viceversa ne tient pas.

Quelques exemples peuvent faire la différence entre les deux opérateurs:

(eq 'a 'a)
T ;; => since two s-expressions (QUOTE A) are “internalized” as the same symbol by the reader.
(eq (list 'a) (list 'a))
NIL ;; => here two lists are generated as different objects in memory
(let* ((l1 (list 'a))
       (l2 l1))
  (eq l1 l2))
T ;; => here there is only one list which is accessed through two different variables
(eq 1 1)
?? ;; it depends on the implementation: it could be either T or NIL if integers are “boxed”
(eq #\a #\a)
?? ;; it depends on the implementation, like for numbers
(eq 2d0 2d0)
?? ;; => dependes on the implementation, but usually is NIL, since numbers in double 
   ;;    precision are treated as structures in many implementations
(let ((a1 2d0)
      (a2 2d0))
  (eq a1 a2))
?? ;; => also in this case the results depends on the implementation

Essayons les mêmes exemples avec EQL :

(eql 'a 'a)
T ;; => equal because they are the same value, as for EQ
(eql (list 'a) (list 'a))
NIL ;; => different because they different objects in memory, as for EQ
(let* ((l1 (list 'a))
       (l2 l1))
  (eql l1 l2))
T ;; => as above
(eql 1 1)
T ;; they are the same number, even if integers are “boxed”
(eql #\a #\a)
T ;; they are the same character
(eql 2d0 2d0)
T ;; => they are the same number, even if numbers in double precision are treated as
   ;;   structures in many implementations
(let ((a1 2d0)
      (a2 2d0))
  (eql a1 a2))
T ;; => as before
(eql 2 2.0)
NIL;; => since the two values are of a different numeric type

À partir des exemples, nous pouvons voir pourquoi l'opérateur EQL devrait être utilisé pour vérifier de manière portable la «similitude» de toutes les valeurs, structurées et non structurées, et pourquoi de nombreux experts déconseillent l'utilisation de l' EQ en général.

Égalité structurelle avec EQUAL, EQUALP, TREE-EQUAL

Ces trois opérateurs implémentent une équivalence structurelle, c'est-à-dire qu'ils vérifient si des objets complexes différents ont une structure équivalente avec un composant équivalent.

EQUAL se comporte comme EQL pour les données non structurées, tandis que pour les structures construites par conses (listes et arbres) et les deux types spéciaux de tableaux, chaînes et vecteurs bit, il effectue une équivalence structurelle , renvoyant true sur deux structures isomorphes et les composants élémentaires sont égaux par EQUAL . Par exemple:

(equal (list 1 (cons 2 3)) (list 1 (cons 2 (+ 2 1))))
T ;; => since the two arguments are both equal to (1 (2 . 3))
(equal "ABC" "ABC")
T ;; => equality on strings
(equal "Abc" "ABC")
NIL ;; => case sensitive equality on strings
(equal '(1 . "ABC") '(1 . "ABC"))
T ;; => equal since it uses EQL on 1 and 1, and EQUAL on "ABC" and "ABC"
(let* ((a (make-array 3 :initial-contents '(1 2 3)))
       (b (make-array 3 :initial-contents '(1 2 3)))
       (c a))
  (values (equal a b)
          (equal a c)))
NIL ;; => the structural equivalence is not used for general arrays
T   ;; => a and c are alias for the same object, so it is like EQL

EQUALP renvoie true dans tous les cas où EQUAL est vrai, mais il utilise également une équivalence structurelle pour les tableaux de tout type et dimension, pour les structures et pour les tables de hachage (mais pas pour les instances de classe!). De plus, il utilise une équivalence insensible à la casse pour les chaînes.

(equalp "Abc" "ABC")
T ;; => case insensitive equality on strings
(equalp (make-array 3 :initial-contents '(1 2 3))
        (make-array 3 :initial-contents (list 1 2 (+ 2 1))))
T ;; => the structural equivalence is used also for any kind of arrays
(let ((hash1 (make-hash-table))
      (hash2 (make-hash-table)))
      (setf (gethash 'key hash1) 42)
      (setf (gethash 'key hash2) 42)
      (print (equalp hash1 hash2))
      (setf (gethash 'another-key hash1) 84)
      (equalp hash1 hash2))   
T   ;; => after the first two insertions, hash1 and hash2 have the same keys and values
NIL ;; => after the third insertion, hash1 and hash2 have different keys and values
(progn (defstruct s) (equalp (make-s) (make-s)))
T ;; => the two values are structurally equal
(progn (defclass c () ()) (equalp (make-instance 'c) (make-instance 'c)))
NIL ;; => two structurally equivalent class instances returns NIL, it's up to the user to
    ;;    define an equality method for classes

Finalement, TREE-EQUAL peut être appliqué aux structures construites par cons et vérifie si elles sont isomorphes, comme EQUAL , mais en laissant à l'utilisateur le choix de la fonction à utiliser pour comparer les leafs, c'est-à-dire cela peut être de tout autre type de données (par défaut, le test utilisé sur atome est EQL ). Par exemple:

(let ((l1 '(1 . ("A" . 2)))
      (l2 '(1 . ("A" . 2))))
  (tree-equal l1 l2 :test #'eql))
NIL ;; => since (eql "A" "A") gives NIL
(let ((l1 '(1 . ("A" . 2)))
      (l2 '(1 . ("A" . 2))))
  (tree-equal l1 l2 :test #'equal))
T ;; since (equal "A" "A") gives T

Opérateurs de comparaison sur des valeurs numériques

Les valeurs numériques peuvent être comparées avec = et les autres opérateurs de comparaison numérique ( /= , < , <= , > , >= ) qui ignorent la différence dans la représentation physique des différents types de nombres et effectuent la comparaison des valeurs mathématiques correspondantes . Par exemple:

(= 42 42)
T ;; => both number have the sme numeric type and the same value
(= 1 1.0 1d0)
T ;; => all the tree values represent the number 1, while for instance (eql 1 1d0) => NIL
  ;;    since it returns true only if the operands have the same numeric type
(= 0.0 -0.0)
T ;; => again, the value is the same, while (eql 0.0 -0.0) => NIL
(= 3.0 #c(3.0 0.0))
T ;; => a complex number with 0 imaginary part is equal to a real number
(= 0.33333333 11184811/33554432)
T ;; => since a float number is passed to RATIONAL before comparing it to another number
  ;; => and (RATIONAL 0.33333333) => 11184811/33554432 in 32-bit IEEE floats architectures
(= 0.33333333 0.33333334)
T ;; => since the result of RATIONAL on both numbers is equal in 32-bit IEEE floats architectures
(= 0.33333333d0 0.33333334d0)
NIL ;; => since the RATIONAL of the two numbers in double precision is different

À partir de ces exemples, nous pouvons conclure que = est l'opérateur qui devrait normalement être utilisé pour effectuer une comparaison entre des valeurs numériques, sauf si nous voulons être strict sur le fait que deux valeurs numériques sont égales seulement si elles ont également le même type numérique, quel cas EQL devrait être utilisé.

Opérateurs de comparaison sur les caractères et les chaînes

Common Lisp dispose de 12 opérateurs de type spécifique pour comparer deux caractères, dont six sensibles à la casse et les autres insensibles à la casse. Leurs noms ont un modèle simple pour se rappeler facilement leur signification:

Sensible aux majuscules et minuscules Insensible à la casse
CHAR = CHAR-EQUAL
CHAR / = CHAR-NOT-EQUAL
CHAR < CHAR-LESSP
CHAR <= CHAR-NOT-GREATERP
CHAR> CHAR-GREATERP
CHAR> = CHAR-NOT-LESSP

Deux caractères d'un même cas sont dans le même ordre que les codes correspondants obtenus par CHAR-CODE , tandis que pour les comparaisons insensibles à la casse, l'ordre relatif entre deux caractères quelconques parmi les deux plages a..z , A..Z dépend de l'implémentation . Exemples:

(char= #\a #\a)
T ;; => the operands are the same character
(char= #\a #\A)
NIL ;; => case sensitive equality
(CHAR-EQUAL #\a #\A)
T ;; => case insensitive equality
(char> #\b #\a)
T ;; => since in all encodings (CHAR-CODE #\b) is always greater than (CHAR-CODE #\a)
(char-greaterp #\b \#A)
T ;; => since for case insensitive the ordering is such that A=a, B=b, and so on,
  ;;    and furthermore either 9<A or Z<0.
(char> #\b #\A)
?? ;; => the result is implementation dependent

Pour les chaînes, les opérateurs spécifiques sont STRING= , STRING-EQUAL , etc. avec le mot STRING au lieu de CHAR. Deux chaînes sont égales si elles ont le même nombre de caractères et si les caractères correspondants sont égaux selon CHAR= ou CHAR-EQUAL si le test est sensible à la casse ou non.

L'ordre entre les chaînes est en ordre lexicographique sur les caractères des deux chaînes. Lorsqu'une comparaison d'ordre réussit, le résultat n'est pas T , mais l'indice du premier caractère dans lequel les deux chaînes diffèrent (ce qui équivaut à vrai, puisque tout objet non NIL est un «booléen généralisé» dans Common Lisp).

Une chose importante est que tous les opérateurs de comparaison sur la chaîne acceptent quatre paramètres de mots-clés: start1 , end1 , start2 , end2 , qui peuvent être utilisés pour restreindre la comparaison à une seule série de caractères contigus. L'index de début, s'il est omis, est 0, l'index de fin est omis est égal à la longueur de la chaîne et la comparaison est effectuée sur la sous-chaîne à partir du caractère index :start et fin avec le caractère indexé :end - 1 inclus.

Enfin, notez qu'une chaîne, même avec un seul caractère, ne peut pas être comparée à un caractère.

Exemples:

(string= "foo" "foo")
T ;; => both strings have the same lenght and the characters are `CHAR=` in order
(string= "Foo" "foo")
NIL ;; => case sensitive comparison
(string-equal "Foo" "foo")
T ;; => case insensitive comparison
(string= "foobar" "barfoo" :end1 3 :start2 3)
T ;; => the comparison is perform on substrings
(string< "fooarr" "foobar")
3 ;; => the first string is lexicographically less than the second one and 
  ;;   the first character different in the two string has index 3
(string< "foo" "foobar")
3 ;; => the first string is a prefix of the second and the result is its length

En tant que cas particulier, les opérateurs de comparaison de chaînes peuvent également être appliqués aux symboles et la comparaison est effectuée sur le SYMBOL-NAME du symbole. Par exemple:

(string= 'a "A")
T ;; since (SYMBOL-NAME 'a) is "A"
(string-equal '|a| 'a)
T ;; since the the symbol names are "a" and "A" respectively

Comme note finale, EQL sur les caractères est équivalent à CHAR= ; EQUAL sur les chaînes est équivalent à STRING= , tandis que EQUALP sur les chaînes est équivalent à STRING-EQUAL .

Survol

Dans Common Lisp, il existe de nombreux prédicats différents pour comparer les valeurs. Ils peuvent être classés dans les catégories suivantes:

  1. Opérateurs d'égalité générique: EQ, EQL, EQUAL, EQUALP. Ils peuvent être utilisés pour des valeurs de tout type et renvoyer toujours une valeur booléenne T ou NIL.
  2. Opérateurs d'égalité de type spécifique: = et = pour les nombres, CHAR = CHAR = CHAR-EQUAL CHAR-NOT-EQUAL pour les caractères, STRING = STRING = STRING-EQUAL STRING-NOT-EQUAL pour les chaînes, TREE-EQUAL pour les conses.
  3. Opérateurs de comparaison pour les valeurs numériques: <, <=,>,> =. Ils peuvent être appliqués à tout type de nombre et comparer la valeur mathématique du nombre, indépendamment du type réel.
  4. Opérateurs de comparaison pour les caractères, comme CHAR <, CHAR-LESSP, etc., qui comparent les caractères de manière sensible à la casse ou insensible à la casse, selon un ordre d'implémentation respectant l'ordre alphabétique naturel.
  5. Les opérateurs de comparaison pour les chaînes, comme STRING <, STRING-LESSP, etc., comparent les chaînes lexicographiquement, soit de manière sensible à la casse, soit insensible à la casse, en utilisant les opérateurs de comparaison de caractères.


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow