Wir müssen nun zum Ende unserer Betrachtungen über Schleifen noch
einmal auf den Gleichheitstest zu sprechen kommen. Nach dem Motto
"Manche sind gleich, und andere noch gleicher" unterscheidet
AutoLISP zwischen verschiedenen Stufen von Gleichheit. Dazu stehen
uns 3 Testfunktionen zur Verfügung:
(= ...),
(eq ...)
und
(equal ...).
(= ...) |
prüft, ob alle Atome numerisch identisch sind. Die Funktion
ist lt. AutoLISP Handbuch nur auf Zahlen und Zeichenketten
anwendbar, Sie können aber leicht feststellen, dass sie sich
auch auf Symbole anwenden lässt. Gegenteilsfunktion
(/= ...) testet genauso, gibt aber das negierte
Ergebnis zurück.
|
(eq ...) |
prüft, ob gebundene Werte identisch sind. Dabei geht es
nicht darum, ob die gebundenen Werte gleich sind, sondern
darum, ob an beide zu prüfenden Symbole das selbe Objekt
gebunden ist. (eq) führt also nicht einen Test auf
Gleichheit, sondern einen Test auf Identität durch. Zwei
Listen, auch wenn sie exakt den selben Inhalt haben, sind
in diesem Sinne nicht identisch. Ist aber eine Liste an
zwei Namen gebunden, gibt (eq) T zurück.
|
(equal ...) |
prüft, ob die Evaluation zweier Ausdrücke zum gleichen
Ergebnis führt. Ein drittes Argument legt eine Toleranzschwelle
fest, wenn Realzahlen verglichen werden. Es können sich bei
Realzahlen immer Abweichungen ab einer bestimmten Stelle
hinter dem Komma ergeben, sodass ein Vergleich mit (=)
fast immer zu einem nil führt, auch wenn die Zahlen eigentlich
gleich sein sollten.
|
Die Unterschiede zwischen den drei Funktionen noch einmal
in einigen Beispielen:
(= (+ 1 1) 2) => T
(eq (+ 1 1) 2) => T
(equal (+ 1 1) 2) => T
; bei numerischer Gleichheit
; ergeben alle 3 Funktionen dasselbe
(setq a 'x b 'x)
(= a b) => T
(eq a b) => T
(equal a b) => T
; Die Symbole sind identisch
; x ist an a und an b gebunden
; Evaluation ergibt Identität
(setq l1 '(x y z))
(setq l2 '(x y z))
(setq l3 l1)
(= l1 l2) => nil
(eq l1 l2) => nil
(equal l1 l2) => T
; Die beiden Listen sind zwar
; inhaltlich gleich, stellen aber
; verschiedene Speicherobjekte
; dar(sonst wären sie nur gemeinsam
; änderbar).
Wenden wir uns noch einmal
(equal ...) zu, und zwar unter
dem Aspekt eines Zahlenvergleichs mit Toleranzschwelle. Wir müssen
damit leben, dass ein Computer nur mit einer endlichen Anzahl von
Kommastellen rechnen kann. Nehmen wir als Bespiel ein Quadrat mit
der Kantenlänge 10: Wir wissen, dass man die Länge der Diagonalen
auf mehrere Weisen berechnen kann. Zwei der Möglichkeiten sind
diese, aber
(equal ...) und
(= ...) geben uns
nil
zurück, wenn wir die Ergebnisse vergleichen:
(* 10(sqrt 2))
=> 14.1421
(/ 10(cos(/ pi 4)))
=> 14.1421
(= (* 10(sqrt 2))(/ 10(cos(/ pi 4))))
=> nil
(equal (* 10(sqrt 2))(/ 10(cos(/ pi 4))))
=> nil
Das liegt daran, dass die beiden Ergebnisse aufgrund der
unterschiedlichen Rechenverfahren nicht exakt gleich sind,
sondern leichte Abweichungen im Nachkommabereich entstehen.
Die Rückgabe auf dem Bildschirm hängt davon ab, wie die
AutoCAD-Systemvariable LUPREC eingestellt ist. Der Maximalwert
ist 8, mehr als 8 Stellen hinter dem Komma werden wir also
nie zu sehen bekommen.
Um solche kleinen Abweichungen, Rundungs- und sonstige Fehler
zu ignorieren, können wir
(equal ...) also mit einem
dritten Argument aufrufen, nämlich der Schwelle. Versuchen wir
es einfach mal mit der 10. Stelle hinter dem Komma:
(equal (* 10(sqrt 2))(/ 10(cos(/ pi 4))) 0.0000000001)
=> T
Auch 14 Stellen hinter dem Komma erhalten wir noch
T
als Ergebnis. Welchen Schwellenwert man verwendet, hängt aber
sehr von verschiedenen Faktoren ab, z.B. entwickeln die
trigonometrischen Funktionen
(sin ...) und
(cos ...)
im Bereich sehr kleiner Winkel recht große Abweichungen. Dann
ist auch die Frage, welche Genauigkeiten überhaupt gebraucht
werden. In einer Maschinenbau-Zeichnung sind zwar Tausendstel
Millimeter von Interesse, aber nicht die 14. Stelle hinter dem
Komma. Hier genügt es sicher, wenn man die Toleranz mit 8 Stellen
angibt. Nimmt man sie allerdings zu groß an, kann es schon
passieren, dass nachher Schraffuren auslaufen usw.