Common Lisp hat viele verschiedene Gleichheitsprädikate.
Das unterscheidet sich von anderen Programmiersprachen, die vielleicht nur ein oder zwei haben (zum Beispiel == und ===).
Einige dieser Prädikate in Common Lisp sind typspezifisch, andere sind generisch.
Hier geht es um die letzteren.
Es gibt vier generische Gleichheitsprädikate, die sich darin unterscheiden, wie streng sie sind, was sie als „gleich“ betrachten.
Sie lauten, von der restriktivsten bis zur am wenigsten restriktiven: eq, eql, equal und equalp.
Eine kurze Zusammenstellung von Definitionen (ein paar Details lassen wir weg) lautet so:
eq: definiert Gleichheit so, dass die beiden Objekte identisch sind.
z. B.:(eq 'a 'a) ; => T
(eq (list 1 2) (list 1 2)) ; => NIL
eql: definiert Gleichheit so, dass zwei Zahlen denselben Typ und denselben Wert haben, zwei Zeichen gleich sind,
oder für alles andere, wenn sie eq sind.
z. B.:(eql 1 1) ; => T
(eql #\a #\a) ; => T
(eql 1 1.0) ; => NIL
(eql #\a #\A) ; => NIL
equal: definiert Gleichheit so: Zwei Listen sind equal, wenn jedes Element ebenfalls equal ist; zwei Strings sind equal, wenn jedes Element eql ist; zwei Arrays sind equal, wenn sie eq sind; alles andere ist equal, wenn es eql ist.
z. B.:(equal (list 1 2) (list 1 2)) ; => T
(equal #(1 2) #(1 2)) ; => NIL
(equal "foo" "foo") ; => T
equalp: definiert Gleichheit so: Strings und Zeichen werden ohne Berücksichtigung der Groß- und Kleinschreibung verglichen; Zahlen werden mit einer gewissen Typumwandlung verglichen; Listen und Arrays sind equalp, wenn jedes Element ebenfalls equalp ist, Strukturen, wenn sie denselben Typ haben und alle Slots equalp sind, und Hash-Tabellen, wenn ihre Schlüssel und Werte equalp sind.
z. B.:(equalp "foo" "FoO") ; => T
(equalp 3 3.0) ; => T
(equalp (make-a-structure :slot1 1 :slot2 2)
(make-a-structure :slot1 1 :slot2 2)) ; => T
Leslie, das Lisp-Alien, programmiert einen Labyrinth-Roboter (wie junge Lisp-Aliens das nun einmal tun) und braucht dabei etwas Hilfe. Das Labyrinth besteht aus einer Reihe von Räumen. Jeder Raum hat mehrere Türen zur Auswahl. Jede Tür braucht einen Schlüssel. Wenn du an einer Tür den falschen Schlüssel benutzt, explodiert sie wahrscheinlich (keine Sorge, dem Roboter passiert nichts, aber die Brandspuren wieder wegzupolieren ist für Leslie ziemlich ärgerlich). Wenn der Roboter keinen Schlüssel hat, der irgendeine Tür öffnet, dann explodiert der ganze Raum! (Noch einmal: keine Sorge, es geht nichts kaputt, aber das Saubermachen ist lästig.)
Du sollst Leslie dabei helfen, dem Roboter den richtigen Schlüssel für jeden Raum zu geben. Der Roboter probiert den Schlüssel der Reihe nach an jeder Tür im Raum aus und geht durch die erste Tür, die sich öffnet.
Jeder Test steht für ein Labyrinth und jede Assertion ist ein Raum. Du musst Prädikatfunktionen schreiben, die zu T ausgewertet werden, wenn die Argumente gleich sind, und zu NIL, wenn nicht.
Dieses Labyrinth hat nur einen einzigen Raum. Du musst eine Funktion bereitstellen, die feststellen kann, ob zwei Objekte dasselbe Objekt sind.
Zum Beispiel:
a ; => "pizza"
b ; => "pizza"
(key-object-identity a a) ; => T
(key-object-identity a b) ; => NIL
denn obwohl a und b gleich aussehen, sind sie nicht dasselbe Objekt.
Dieses Labyrinth hat zwei Räume. Der erste hat eine Tür, die sich öffnen lässt, wenn dein Schlüssel feststellen kann, ob zwei Zahlen strikt gleich sind. Der zweite Raum braucht einen Schlüssel, der beim Prüfen, ob Zahlen gleich sind, etwas flexibler ist.
Zum Beispiel:
a ; => 13
b ; => 13.0
c ; => 13
(key-numbers a b) ; => NIL
(key-numbers a c) ; => T
(key-numbers-of-different-types a b) ; => T
denn obwohl a und b numerisch gleich sind, haben sie verschiedene Typen.
Dieses Labyrinth hat zwei Räume. Der erste hat eine Tür, die sich öffnen lässt, wenn dein Schlüssel feststellen kann, ob zwei Zeichen strikt gleich sind. Der zweite Raum braucht einen Schlüssel, der einen Vergleich der Zeichen ohne Beachtung der Groß- und Kleinschreibung erlaubt.
Zum Beispiel:
a ; => #\X
b ; => #\x
c ; => #\X
(key-characters a b) ; => NIL
(key-characters a c) ; => T
(key-characters-case-insensitively a b) ; => T
denn nur sehr großzügige Gleichheitsprädikate ignorieren die Groß- und Kleinschreibung, wenn sie zwei Zeichen vergleichen.
Wie im Labyrinth der Zeichen braucht der erste Raum einen Schlüssel, der prüft, ob zwei Strings gleich sind; der zweite Raum braucht einen Schlüssel, der einen Vergleich ohne Beachtung der Groß- und Kleinschreibung erlaubt.
Zum Beispiel:
a ; => "pizza"
b ; => "PIZZA"
c ; => "pizza"
(key-strings a b) ; => NIL
(key-strings a c) ; => T
(key-strings-case-insensitively a b) ; => T
denn nur sehr großzügige Gleichheitsprädikate ignorieren die Groß- und Kleinschreibung, wenn sie zwei Strings vergleichen.
Dies ist ein großes Labyrinth mit vielen Räumen. Jeder Raum braucht einen Schlüssel, der prüft, ob die Conses die Dinge, die gleich sind, enthalten. Jeder Raum prüft die Gleichheit von Conses mit unterschiedlichen Inhalten: Symbole, Zahlen, Zeichen, und für manche brauchst du flexiblere Gleichheitsdefinitionen für diese Inhalte.
Zum Beispiel:
syms-a ; => (left . right)
syms-b ; => (up . down)
syms-c ; => (left . right)
chars-a ; => (#\x . #\y)
chars-b ; => (#\x . #\Y)
chars-c ; => (#\x . #\y)
nums-a ; => (13 . 23)
nums-b ; => (13 . 23.0)
nums-c ; => (13 . 23)
(key-conses-of-symbols syms-a syms-b) ; => NIL
(key-conses-of-symbols syms-a syms-c) ; => T
(key-conses-of-characters chars-a chars-b) ; => NIL
(key-conses-of-characters chars-a chars-c) ; => T
(key-conses-of-characters-case-insensitively
chars-a chars-b) ; => T
(key-conses-of-numbers nums-a nums-b) ; => NIL
(key-conses-of-numbers nums-a nums-c) ; => T
(key-conses-of-numbers-of-different-types
nums-a nums-b) ; => T
denn selbst ein Gleichheitsprädikat, das in ein Cons hineinschaut, ist vielleicht noch nicht großzügig genug, um beim Vergleichen die Groß- und Kleinschreibung oder den numerischen Typ zu ignorieren.
Dieses Labyrinth ist einfacher und hat nur zwei Räume. Der erste braucht einen Schlüssel, der prüft, ob die Arrays dieselben Arrays sind. Der zweite braucht einen Schlüssel, der den Inhalt der Arrays prüft.
Zum Beispiel:
a ; => #[13 23]
b ; => #[13 23]
c ; => #[13 23.0]
(key-arrays a b) ; => NIL
(key-arrays a c) ; => NIL
(key-arrays-loosely a b) ; => T
(key-arrays-loosely a c) ; => T
denn nur sehr großzügige Gleichheitsprüfer stellen fest, ob die Inhalte der Arrays gleich sind.
Melde dich bei Exercism an, um Common Lisp mit 27 Konzepte92 Übungen und echtem menschlichen Mentoring zu lernen und zu meistern, alles kostenlos.