Suche…


Der Unterschied zwischen EQ und EQL

  1. EQ prüft, ob zwei Werte die gleiche Speicheradresse haben. Mit anderen Worten, es wird geprüft, ob die beiden Werte tatsächlich das gleiche , identische Objekt sind. Es handelt sich also um den Identitätstest und sollte nur auf Strukturen angewendet werden: konses, Arrays, Strukturen, Objekte. In der Regel sehen Sie, ob Sie tatsächlich mit demselben Objekt zu tun haben, das über verschiedene Pfade „erreicht“ wird oder durch ein Aliasing durcheinander verläuft verschiedene Variablen.

  2. EQL prüft, ob zwei Strukturen dasselbe Objekt sind (wie EQ ) oder ob es sich um dieselben nicht strukturierten Werte handelt (d. EQL numerischen Werte für Zahlen des gleichen Typs oder die Zeichenwerte). Da er den EQ Operator enthält und auch für nicht strukturierte Werte verwendet werden kann, ist er der wichtigste und am häufigsten verwendete Operator, und fast alle primitiven Funktionen, die einen Gleichheitsvergleich erfordern, wie beispielsweise MEMBER , verwenden diesen Operator standardmäßig .

Es ist also immer wahr, dass (EQ XY) (EQL XY) impliziert, während die Umkehrung nicht gilt.

Einige Beispiele können den Unterschied zwischen den beiden Operatoren deutlich machen:

(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

Lass uns die gleichen Beispiele mit EQL versuchen:

(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

An den Beispielen können wir sehen, warum der EQL Operator verwendet werden sollte, um portabel auf „Gleichheit“ für alle strukturierten und nicht strukturierten Werte zu prüfen und warum eigentlich viele Experten von der Verwendung von EQ generell abraten.

Strukturelle Gleichheit mit EQUAL, EQUALP, TREE-EQUAL

Diese drei Operatoren implementieren die strukturelle Äquivalenz, dh sie prüfen, ob verschiedene, komplexe Objekte eine äquivalente Struktur mit einer äquivalenten Komponente haben.

EQUAL verhält sich wie EQL für nicht strukturierte Daten, während für Strukturen, die durch Konsolen (Listen und Bäume) und die beiden speziellen Typen von Arrays, Strings und Bit-Vektoren erstellt werden, eine strukturelle Äquivalenz ausgeführt wird , die auf zwei isomorphen und deren Strukturen true zurückgibt Elementarkomponenten sind bei EQUAL entsprechend gleich. Zum Beispiel:

(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 gibt in allen Fällen, in denen EQUAL wahr ist, true zurück, verwendet jedoch auch die strukturelle Äquivalenz für Arrays jeder Art und Dimension, für Strukturen und für Hashtabellen (nicht jedoch für Klasseninstanzen!). Darüber hinaus wird für Zeichenfolgen die Groß- und Kleinschreibung verwendet.

(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

TREE-EQUAL kann schließlich auf Strukturen angewendet werden, die durch Ein-und cons , ob diese isomorph sind, wie EQUAL Die Wahl der zu verwendenden Funktion für den Vergleich der Blätter, dh des angetroffenen Nicht-Kons (Atom), TREE-EQUAL dem Benutzer überlassen. EQL kann ein beliebiger anderer Datentyp sein (standardmäßig wird EQL für atom EQL ). Zum Beispiel:

(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

Vergleichsoperatoren für numerische Werte

Numerische Werte können mit = und den anderen numerischen Vergleichsoperatoren ( /= , < , <= , > , >= ) verglichen werden, die den Unterschied in der physikalischen Darstellung der verschiedenen Arten von Zahlen ignorieren und den Vergleich der entsprechenden mathematischen Werte durchführen . Zum Beispiel:

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

Aus diesen Beispielen können wir folgern, dass = der Operator ist, der normalerweise zum Vergleich zwischen numerischen Werten verwendet werden sollte, es sei denn, wir möchten streng darauf achten, dass zwei numerische Werte nur dann gleich sind , wenn sie denselben numerischen Typ in haben In welchem ​​Fall sollte EQL verwendet werden.

Vergleichsoperatoren für Zeichen und Strings

Common Lisp verfügt über 12 typspezifische Operatoren, um zwei Zeichen zu vergleichen, von denen 6 die Groß- und Kleinschreibung und die anderen nicht die Groß- und Kleinschreibung berücksichtigt. Ihre Namen haben ein einfaches Muster, um sich an ihre Bedeutung zu erinnern:

Groß- / Kleinschreibung Groß- und Kleinschreibung
CHAR = CHAR-EQUAL
CHAR / = CHAR-NOT-GLEICH
CHAR < CHAR-LESSP
CHAR <= CHAR-NOT-GREATERP
CHAR> CHAR-GREATERP
CHAR> = CHAR-NOT-LESSP

Zwei Zeichen desselben Falls sind in derselben Reihenfolge wie die entsprechenden Codes, die durch CHAR-CODE , während bei Vergleichen ohne a..z Groß- und Kleinschreibung die relative Reihenfolge zwischen zwei beliebigen Zeichen aus den beiden Bereichen a..z , A..Z Implementierung abhängt . Beispiele:

(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

Für Strings sind die spezifischen Operatoren STRING= , STRING-EQUAL usw. mit dem Wort STRING anstelle von CHAR. Zwei Zeichenfolgen sind gleich, wenn sie dieselbe Anzahl von Zeichen haben, und die entsprechenden Zeichen sind gemäß CHAR= oder CHAR-EQUAL wenn bei dem Test die Groß- / Kleinschreibung beachtet wird oder nicht.

Die Reihenfolge zwischen den Saiten ist die lexikographische Reihenfolge der Zeichen der beiden Saiten. Wenn ein Sortiervergleich erfolgreich ist, ist das Ergebnis nicht T , sondern der Index des ersten Zeichens, in dem sich die beiden Zeichenfolgen unterscheiden (was mit true übereinstimmt, da jedes Nicht-NIL-Objekt in Common Lisp ein „generalized boolean“ ist).

Wichtig ist, dass alle Vergleichsoperatoren für string vier Schlüsselwortparameter akzeptieren: start1 , end1 , start2 , end2 , mit denen der Vergleich auf eine zusammenhängende Zeichenfolge innerhalb einer oder beider Zeichenfolgen beschränkt werden kann. Wenn der Startindex weggelassen wird, ist der Startindex 0, der Endindex wird weggelassen und entspricht der Länge des Strings. Der Vergleich wird für den Teilstring durchgeführt, der mit dem Zeichen mit Index :start und endet mit dem Zeichen mit Index :end - 1 eingeschlossen.

Beachten Sie schließlich, dass eine Zeichenfolge auch mit einem einzelnen Zeichen nicht mit einem Zeichen verglichen werden kann.

Beispiele:

(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

Als Sonderfall können die String-Vergleichsoperatoren auch auf Symbole angewendet werden, und der Vergleich wird am SYMBOL-NAME des Symbols vorgenommen. Zum Beispiel:

(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

Als letzte Bemerkung entspricht EQL für Zeichen CHAR= ; EQUAL für Strings entspricht STRING= , EQUALP für Strings entspricht STRING-EQUAL .

Übersicht

In Common Lisp gibt es viele verschiedene Prädikate für den Vergleich von Werten. Sie können in folgende Kategorien eingeteilt werden:

  1. Generische Gleichheitsoperatoren: EQ, EQL, EQUAL, EQUALP. Sie können für Werte eines beliebigen Typs verwendet werden und geben immer einen booleschen Wert T oder NIL zurück.
  2. Geben Sie spezifische Gleichheitsoperatoren ein: = und = für Zahlen, CHAR = CHAR = CHAR-EQUAL CHAR-NOT-EQUAL für Zeichen, STRING = STRING = STRING-EQUAL STRING-NOT-EQUAL für Zeichenfolgen, TREE-EQUAL für Konsolen.
  3. Vergleichsoperatoren für numerische Werte: <, <=,>,> =. Sie können auf jeden Zahlentyp angewendet werden und vergleichen den mathematischen Wert der Zahl unabhängig vom tatsächlichen Typ.
  4. Vergleichsoperatoren für Zeichen wie CHAR <, CHAR-LESSP usw., die Zeichen entweder nach Groß- oder Kleinschreibung oder nach Groß- und Kleinschreibung vergleichen, je nach implementierungsabhängiger Reihenfolge, die die natürliche alphabetische Reihenfolge beibehält.
  5. Vergleichsoperatoren für Strings wie STRING <, STRING-LESSP usw., die Strings lexikographisch entweder case case oder in case case nicht vergleichen, indem Sie die Zeichenvergleichsoperatoren verwenden.


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow