Szukaj…


Różnica między EQ a EQL

  1. EQ sprawdza, czy dwie wartości mają ten sam adres pamięci: innymi słowy, sprawdza, czy te dwie wartości są w rzeczywistości tym samym , identycznym obiektem. Można go zatem uznać za test tożsamości i należy go stosować tylko do struktur: stożków, tablic, struktur, obiektów, zwykle w celu sprawdzenia, czy faktycznie ma się do czynienia z tym samym obiektem „osiągniętym” różnymi ścieżkami lub aliasowanym przez różne zmienne.

  2. EQL sprawdza, czy dwie struktury są tym samym obiektem (jak EQ ) lub czy są to te same niestrukturalne wartości (to znaczy te same wartości liczbowe dla liczb tego samego typu lub wartości znakowych). Ponieważ zawiera on operator EQ i może być używany również w przypadku wartości niestrukturalnych, jest najważniejszym i najczęściej używanym operatorem, a prawie wszystkie podstawowe funkcje, które wymagają porównania równości, takie jak MEMBER , domyślnie używają tego operatora .

Tak więc zawsze jest prawdą, że (EQ XY) implikuje (EQL XY) , podczas gdy viceversa się nie trzyma.

Kilka przykładów może wyjaśnić różnicę między dwoma operatorami:

(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

Spróbujmy tych samych przykładów z 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

Z przykładów możemy zobaczyć, dlaczego operator EQL powinien być używany do przenośnego sprawdzania „identyczności” wszystkich wartości, ustrukturyzowanych i EQL , i dlaczego tak naprawdę wielu ekspertów odradza stosowanie EQ w ogóle.

Równość strukturalna z EQUAL, EQUALP, TREE-EQUAL

Te trzy operatory implementują równoważność strukturalną, to znaczy sprawdzają, czy różne złożone obiekty mają równoważną strukturę z równoważnym składnikiem.

EQUAL zachowuje się jak EQL dla danych EQL , podczas gdy dla struktur zbudowanych z szyszek (list i drzew) oraz dwóch specjalnych typów tablic, łańcuchów i wektorów bitowych, wykonuje równoważność strukturalną , zwracając wartość true dla dwóch struktur, które są izomorficzne i których elementy elementarne są odpowiednio równe EQUAL . Na przykład:

(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 zwraca true we wszystkich przypadkach, w których EQUAL jest prawdziwy, ale wykorzystuje również równoważność strukturalną dla tablic dowolnego rodzaju i wymiarów, dla struktur i tablic mieszających (ale nie dla instancji klas!). Ponadto wykorzystuje ciągi znaków bez rozróżniania wielkości liter.

(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

Wreszcie, TREE-EQUAL można zastosować do struktur zbudowanych za pomocą cons i sprawdza, czy są one izomorficzne, jak EQUAL , ale pozostawiając użytkownikowi wybór, której funkcji użyć do porównania liści, tj. Napotkany brak wad (atom), który może być dowolnego innego typu danych (domyślnie testem atomowym jest EQL ). Na przykład:

(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

Operatory porównania wartości liczbowych

Wartości liczbowe można porównać z = i innymi operatorami porównania numerycznego ( /= , < , <= , > , >= ), które ignorują różnicę w fizycznej reprezentacji różnych typów liczb i wykonują porównanie odpowiednich wartości matematycznych . Na przykład:

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

Z tych przykładów możemy wywnioskować, że = jest operatorem, którego normalnie należy używać do przeprowadzania porównania wartości liczbowych, chyba że chcemy ściśle przestrzegać faktu, że dwie wartości liczbowe są równe tylko wtedy, gdy mają również ten sam typ liczbowy, w w którym przypadku należy użyć EQL .

Operatory porównania znaków i ciągów

Common Lisp ma 12 operatorów specyficznych dla typu, aby porównać dwa znaki, 6 z nich rozróżnia duże i małe litery. Ich nazwy mają prosty wzór, aby ułatwić zapamiętanie ich znaczenia:

Z uwzględnieniem wielkości liter Bez rozróżniania wielkości liter
CHAR = CHAR-EQUAL
CHAR / = CHAR-NOT-EQUAL
CHAR < CHAR-LESSP
CHAR <= CHAR-NOT-GREATERP
CHAR> CHAR-GREATERP
CHAR> = CHAR-NOT-LESSP

Dwa znaki tego samego przypadku są w tej samej kolejności, co odpowiednie kody uzyskane za pomocą CHAR-CODE , natomiast w przypadku porównań bez rozróżniania wielkości liter względna kolejność między dowolnymi dwoma znakami wziętymi z dwóch zakresów a..z , A..Z zależy od implementacji . Przykłady:

(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

W przypadku łańcuchów konkretnymi operatorami są STRING= , STRING-EQUAL itp. Ze słowem STRING zamiast CHAR. Dwa ciągi znaków są równe, jeśli mają taką samą liczbę znaków, a odpowiadające im znaki są równe zgodnie z CHAR= lub CHAR-EQUAL jeśli w teście rozróżniana jest wielkość liter.

Porządek między ciągami znaków jest porządkiem leksykograficznym na znakach dwóch ciągów. Gdy porównanie porządków się powiedzie, wynikiem nie jest T , ale indeks pierwszego znaku, w którym oba łańcuchy się różnią (co jest równoważne z prawdą, ponieważ każdy obiekt inny niż NIL jest „uogólnioną wartością logiczną” w Common Lisp).

Ważną rzeczą jest to, że wszystkie operatory porównania na ciąg przyjmować cztery parametry słów kluczowych: start1 , end1 , start2 , end2 , które mogą być wykorzystywane do ograniczania porównanie tylko do ciągłym biegu znaków wewnątrz jednego lub obu łańcuchów. Indeks początkowy, jeśli pominięty, wynosi 0, indeks końcowy pominięty jest równy długości łańcucha, a porównanie w podciągu rozpoczynającym się od znaku o indeksie :start i kończącego się na znaku o indeksie :end - 1 włącznie.

Na koniec zwróć uwagę, że łańcucha, nawet z jednym znakiem, nie można porównać do znaku.

Przykłady:

(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

W szczególnym przypadku operatory porównania łańcucha można również zastosować do symboli, a porównania dokonuje się na SYMBOL-NAME tego symbolu. Na przykład:

(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

Na koniec, EQL dla znaków jest równoważne CHAR= ; EQUAL na łańcuchach jest równoważny STRING= , natomiast EQUALP na łańcuchach jest równoważny STRING-EQUAL .

Omówienie

We Common Lisp istnieje wiele różnych predykatów do porównywania wartości. Można je podzielić na następujące kategorie:

  1. Ogólne operatory równości: EQ, EQL, EQUAL, EQUALP. Mogą być używane do wartości dowolnego typu i zawsze zwracają wartość logiczną T lub NIL.
  2. Typowe operatory równości: = i = dla liczb, CHAR = CHAR = CHAR-EQUAL CHAR-NOT-EQUAL dla znaków, STRING = STRING = STRING-EQUAL STRING-NOT-EQUAL dla łańcuchów, TREE-EQUAL dla stożków.
  3. Operatory porównania dla wartości liczbowych: <, <=,>,> =. Można je zastosować do dowolnego rodzaju liczby i porównać wartość matematyczną liczby niezależnie od rzeczywistego typu.
  4. Operatory porównania dla znaków, takich jak CHAR <, CHAR-LESSP itp., Które porównują znaki albo z rozróżnianiem wielkości liter, albo bez rozróżniania wielkości liter, zgodnie z kolejnością zależną od implementacji, która zachowuje naturalną kolejność alfabetyczną.
  5. Operatory porównania ciągów, takie jak STRING <, STRING-LESSP itp., Które porównują ciągi leksykograficzne, albo w sposób rozróżniania wielkości liter, albo w sposób niewrażliwy na wielkość liter, za pomocą operatorów porównania znaków.


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow