Das Gleiche oder dasselbe: eql, equal und deren Freunde

Common Lisp hat eine ganze Reihe unterschiedlicher Vergleichsoperatoren. Auf den ersten Blick ist das verwirrend, aber wichtig zu wissen. Die Operatoren unterscheiden sich darin, was sie als „gleich“ erkennen. Dazu gibt es die universellen Funktionen

  • eq
  • eql
  • equal
  • equalp

In der gezeigten Reihenfolge lockert Lisp die Regeln, wann zwei „Objekte“ als gleich betrachtet werden. Die Abstufungen reichen dabei von „die Objekte sind identisch“ bis hin zu „die Objekte sind eher ähnlicch statt gleich

eq – Identität statt Gleichheit

Die am meisten strikte Prüfung nimmt eq vor. Es liefert nur dann t, wenn die zu prüfenden Objekte identisch sind – sie verweisen auf ein und dasselbe Objekt im Speicher.

(eq 'a 'a) ; --> T
(eq 1 1)   ; --> T
(eq 1 1.0) ; --> NIL 
(eq "A" "A") ; --> NIL
(eq (list 1 2) (list 1 2)) ; --> NIL

Merke: eq prüft Identität, NICHT Gleichheit

Wichtig ist, das die beiden Strings „A“ aus dem Beispiel NICHT gleich bezüglich ‚eq‘ sind, dieses Wissen wird uns später noch nützen.

eql – leichte Lockerung der Regeln

Zwei Lisp-Objekte a und b sind eql (liefern also T im Vergleich), wenn eine der folgenden Bedingungen erfüllt sind:

  1. a und b sind „eq“. Beide Objekte sind identisch (s. o.)
  2. a und b sind Zahlenwerte gleichen Typs und gleichen Wertes. Ein Integer 1 und ein Float 1.0 sind dementsprechend NICHT eql
  3. a und b sind vom Typ Character und repräsentieren den gleichen Characterwert

Wichtig zu wissen ist, dass eql bei vielen Funktionen in Common Lisp der Defaultvergleichsoperator ist. Beispiele dazu sind:

  • Vergleiche von Schlüsselwerten bei Hashtabellen. Dies kann beim Anlegen einer neuen Hashtabelle (make-hash-table) mit dem Parameter :test geändert werden.

Gerade der Einsatz in einer Hashtable liefert dadurch überraschende Ergebnisse, wenn die Schlüsselwerte Strings sind:

(defvar *h* (make-hash-table)) 
(setf (gethash "one" *h*) 1) 
(gethash "one" *h*)  ;; --> NIL. Warum????

;; Darum
(eql "one" "one") ;; --> NIL

Wenn die Schlüssel Strings seien sollen, dann darf die Hashtable nicht mit eql als Testfunktion angelegt werden. Hier bietet sich dann an, equal zu benutzen:

(setq *h* (make-hash-table :test 'equal))

equal – Inhaltliche Gleichheit

TBD.

equalp – Mehr „ähnlich“ als „gleich“

TBD.

Die Spezialisierten

= für Zahlen

string= und string-equal für Zeichenketten

Wie der Name schon vermuten lässt, vergleicht string= zwei Strings auf Gleichheit. Optional lässt sich dabei mit :start1, :end1 und :start2, :end2 jeweils ein zu vergleichender Teilbereich der beiden Strings angeben. Um als „Gleich“ zu gelten, müssen zwei Strings (oder die ausgewählten Substrings)

  1. die gleiche Länge haben
  2. An allen Positionen die Zeichen gleich sein (char=)

string-equal weicht das zweite Kriterium dahin gehend auf, dass die Zeichen ohne Berücksichtigung von Groß- und Kleinschreibung verglichen werden (die Zeichen sind char-equal statt char=)

char= und char-equal für Zeichen

char= liefert T, wenn alle übergebenen Zeichen gleich sind. char-equal macht das gleiche, ignoriert dabei jedoch Groß- und Kleinschreibung.