Sök…


Skillnaden mellan EQ och EQL

  1. EQ kontrollerar om två värden har samma minnesadress: med andra ord, den kontrollerar om de två värdena faktiskt är samma , identiska objekt. Så, det kan anses identitetstest och bör endast tillämpas på strukturer: conses, arrayer, strukturer, föremål, oftast för att se om du arbetar i själva verket med samma objekt ”nådde” genom olika vägar, eller alias genom olika variabler.

  2. EQL kontrollerar om två strukturer är samma objekt (som EQ ) eller om de är samma icke-strukturerade värden (det vill säga samma numeriska värden för nummer av samma typ eller teckenvärden). Eftersom den inkluderar EQ operatören och även kan användas på icke-strukturerade värden, är den viktigaste och mest använda operatören, och nästan alla primitiva funktioner som kräver en jämlik jämförelse, som MEMBER , använder som standard denna operatör .

Så det är alltid sant att (EQ XY) antyder (EQL XY) , medan viceversa inte håller.

Några exempel kan tydliggöra skillnaden mellan de två operatörerna:

(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

Låt oss prova samma exempel med 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

Från exemplen kan vi se varför EQL operatören bör användas för att portabelt kontrollera för "likhet" för alla värden, strukturerade och icke-strukturerade, och varför faktiskt många experter rekommenderar att använda EQ i allmänhet.

Strukturell jämlikhet med EQUAL, Equalp, Tree-Equal

Dessa tre operatörer implementerar strukturell ekvivalens, det vill säga de kontrollerar om olika, komplexa objekt har ekvivalent struktur med motsvarande komponent.

EQUAL uppför sig som EQL för icke-strukturerad data, medan för strukturer byggda av konser (listor och träd), och de två speciella typerna av matriser, strängar och bitvektorer, utför den strukturell ekvivalens och returnerar sant på två strukturer som är isomorfa och vars elementära komponenter är motsvarande lika med EQUAL . Till exempel:

(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 returnerar sant i alla fall där EQUAL är sant, men det använder också strukturell ekvivalens för matriser av alla slag och dimensioner, för strukturer och för hashtabeller (men inte för klassinstanser!). Dessutom använder det fallljuskänslig ekvivalens för strängar.

(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

Slutligen kan TREE-EQUAL tillämpas på strukturer byggda genom cons och kontrollerar om de är isomorfa, liksom EQUAL , men lämnar åt användaren valet av vilken funktion som ska användas för att jämföra löven, dvs. som kan vara av någon annan datatyp (som standard är testet som används på atom EQL ). Till exempel:

(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

Jämför operatörer med numeriska värden

Numeriska värden kan jämföras med = och de andra operatörerna för numerisk jämförelse ( /= , < , <= , > , >= ) som ignorerar skillnaden i fysisk representation av de olika typerna av siffror och utför jämförelse av motsvarande matematiska värden . Till exempel:

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

Från dessa exempel kan vi dra slutsatsen att = är den operatör som normalt bör användas för att utföra jämförelse mellan numeriska värden, såvida vi inte vill vara strikta på att två numeriska värden är lika endast om de också har samma numeriska typ, i vilket fall EQL ska användas.

Jämför operatörer på tecken och strängar

Common Lisp har 12 typspecifika operatörer för att jämföra två tecken, 6 av dem är känsliga och de övriga är känsliga. Deras namn har ett enkelt mönster för att göra det lätt att komma ihåg sin betydelse:

Skiftlägeskänsliga Fallet okänslig
CHAR = CHAR-Equal
CHAR / = CHAR-NOT-Equal
CHAR < CHAR-LESSP
CHAR <= CHAR-NOT-GREATERP
CHAR> CHAR-GREATERP
CHAR> = CHAR-NOT-LESSP

Två tecken i samma fall är i samma ordning som motsvarande koder erhållna med CHAR-CODE , medan för falllösa jämförelser är den relativa ordningen mellan alla två tecken som tas från de två intervall a..z , A..Z implementeringsberoende . Exempel:

(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 strängar är de specifika operatörerna STRING= , STRING-EQUAL etc. med ordet STRING istället för CHAR. Två strängar är lika om de har samma antal tecken och motsvarande tecken är lika enligt CHAR= eller CHAR-EQUAL om testet är skiftlägeskänsligt eller inte.

Ordningen mellan strängarna är den leksikografiska ordningen på karaktärerna i de två strängarna. När en beställningsjämförelse lyckas är resultatet inte T , utan indexet för det första tecknet i vilket de två strängarna skiljer sig (vilket motsvarar sant, eftersom alla icke-NIL-objekt är ett "generaliserat boolean" i Common Lisp).

En viktig sak är att alla jämförelseoperatörer på sträng accepterar fyra nyckelordsparametrar: start1 , end1 , start2 , end2 , som kan användas för att begränsa jämförelsen till endast en sammanhängande körning av tecken i en eller båda strängarna. Startindexet om utelämnat är 0, slutindexet utelämnas är lika med strängens längd, och jämförelsen som utförs på substrängen börjar vid tecken med index :start och avslutas med tecknet med index :end - 1 ingår.

Observera slutligen att en sträng, även med ett enda tecken, inte kan jämföras med ett tecken.

Exempel:

(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

Som ett speciellt fall kan strängen jämförelseoperatörer också tillämpas på symboler, och jämförelsen görs på SYMBOL-NAME . Till exempel:

(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

Som sista anmärkning är EQL på tecken ekvivalent med CHAR= ; EQUAL på strängar motsvarar STRING= , medan EQUALP på strängar motsvarar STRING-EQUAL .

översikt

I Common Lisp finns det många olika predikat för att jämföra värden. De kan klassificeras i följande kategorier:

  1. Generiska jämställdhetsoperatörer: EQ, EQL, EQUAL, EQUALP. De kan användas för värden av valfri typ och returnerar alltid ett booleskt värde T eller NIL.
  2. Ange specifika jämställdhetsoperatörer: = och = för siffror, CHAR = CHAR = CHAR-EQUAL CHAR-NOT-Equal för tecken, STRING = STRING = STRING-EQUAL STRING-NOT-Equal för strängar, TREE-EQUAL för konserter.
  3. Jämförelseoperatörer för numeriska värden: <, <=,>,> =. De kan tillämpas på alla typer av nummer och jämföra det matematiska värdet på antalet, oberoende av den faktiska typen.
  4. Jämförelseoperatörer för tecken, som CHAR <, CHAR-LESSP, etc., som jämför tecken antingen på ett skiftlägeskänsligt sätt eller på ett obetydligt sätt, enligt en implementeringsberoende ordning som bevarar den naturliga alfabetiska ordningen.
  5. Jämförelseoperatörer för strängar, som STRING <, STRING-LESSP, etc., som jämför leksikografiska strängar, antingen på ett skiftlägeskänsligt sätt eller på ett okänsligt sätt, med hjälp av teckenjämförelseoperatörer.


Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow