Eines der in den vorigen Kapiteln erwähnten Kriterien für
eine Funktion war, dass sie in der Lage sein muss, auf
verschiedene Situationen flexibel zu reagieren. Das ist
natürlich schon dadurch gegeben, dass sie verschiedene Werte
für ihre Argumente erhält. Eine Funktion, die immer gleich
reagiert, kann aber auch sinnvoll sein. Wir kennen ja bereits
(textscr). Diese Funktion bewirkt immer nur ein
Umschalten auf den Textbildschirm (bzw. das Textfenster)
und ist dabei absolut unflexibel.
Man muss aber berücksichtigen, dass es hier um eine
hundertprozentige Nebeneffekt-Funktion geht. Funktionen,
deren Haupteffekte immer gleich sind, und die keine
Nebeneffekte haben, sind jedoch absolut sinnlos, da man sie
auch als Konstanten wie z.B. pi definieren könnte. Man
müsste also einfach den Wert an ein Symbol binden und hätte
den gleichen Nutzen. Beispiel: Eine Funktion, die immer Wurzel
aus 2 berechnet, kann durch eine Konstante ersetzt werden:
(defun wurzel-zwei()(sqrt 2))
;ist Unsinn und sollte durch
(setq wurzel-zwei (sqrt 2))
ersetzt werden. Am Rande: Man kann in AutoLisp keine echten
Konstanten erzeugen, deren Wert sich nicht mehr ändern lässt.
Das Wort Konstante ist also eine blosse Absichtserklärung
des Programmierers, den Wert einer Variablen nach dem Setzen
nicht mehr zu ändern.
Wie kann man nun aber noch mehr Flexibilität in Funktionen
hineinbringen? Was wir brauchen, ist eine Möglichkeit der
Entscheidung in der Art WENN dies-der-Fall-ist DANN tue-dieses
SONST tue-jenes. Erst eine solche Entscheidungsfreiheit macht
es möglich, wirklich sinnvolle Funktionen zu schreiben.
LISP stellt uns dazu die Funktion
(if ...) zur Verfügung,
die entweder zwei oder drei Argumente erhält:
(if <ausdruck1> <ausdruck2> [<ausdruck3>])
ausdruck3 kann vorhanden sein oder nicht, dies wird durch
die eckigen Klammern angezeigt.
ausdruck1 kann ein völlig beliebiger Ausdruck sein, der
entweder nil oder etwas anderes zurückgibt. Gibt er etwas anderes
als nil zurück, wird dann
ausdruck2 evaluiert, anderenfalls
wird (sofern vorhanden)
ausdruck3 evaluiert. In der Praxis
werden in
ausdruck1 meistens irgendwelche Testfunktionen
eingesetzt, von denen LISP eine ganze Reihe zur Verfügung stellt.
Wir sollten uns daher zunächst mit einigen dieser Testfunktionen
befassen:
(> x y) testet, ob x grösser als y ist
(< x y) testet, ob x kleiner als y ist
(= x y) testet, ob x gleich y ist
Diese Funktionen können übrigens auch mit mehr als 2 Argumenten
aufgerufen werden. So testet z.B.
(> x y z), ob
x
grösser als
y und
y grösser als
z ist. Mit
anderen Worten: Es wird getestet, ob
y zwischen
x
und
z liegt.
Dann gibt es auch noch kombinierte Formen:
(>= x y) testet, ob x grösser oder gleich y ist
(<= x y) testet, ob x kleiner oder gleich y ist
Schliesslich haben wir auch noch einen Test auf Ungleichheit:
(/= x y) testet, ob x ungleich y ist
Die folgenden Funktionen bietet noch eine zusätzliche Möglichkeit:
(zerop x)
; ist äquivalent zu
(= x 0)
testet, ein Wert gleich 0 ist.
Die bisher genannten Funktionen lassen sich auf Zahlen und auch
auf Zeichenketten anwenden (
zerop ist nur für Zahlen gedacht).
Der Test auf Gleichheit bzw. Ungleichheit lässt sich aber auch auf
Symbole anwenden.
Allen Testfunktionen ist gemeinsam, dass sie - wenn das Testergebnis
logisch wahr ist - das Symbol
T zurückgeben, im anderen Fall
erhalten wir
nil. Da jede Rückgabe ausser
nil
gleichbedeutend mit 'wahr' ist, kann es uns eigentlich auch ganz
egal sein, was diese Funktionen zurückgeben, solange es - ja nach
Testergebnis -
nil oder etwas anderes ist.
Dies ist aber deshalb von besonderer Bedeutung, da sich das Symbol
T um- oder wegdefinieren lässt, zumindest ist dies bis AutoCAD
14 der Fall. Ab AutoCAD 2000 ist das Symbol
T geschützt und
unveränderlich. Wir tun so etwas natürlich nicht absichtlich, aber
es kann trotzdem passieren. Normalerweise ist der an das Symbol
T gebundene Wert ebenfalls das Symbol
T (Selbstbindung).
Ist
T aber versehentlich umdefiniert worden, dann hat
T
den Wert
nil (oder irgendeinen anderen Wert).
Da die Testfunktionen aber immer das Symbol
T und nicht dessen
Wert zurückgeben, macht es nichts, wenn
T umdefiniert wurde.
Wenn Sie aber selbst weitere Testfunktionen schreiben, müssen Sie
darauf achten, das Symbol
T selbst (quotiert: also
'T)
zurückzugeben und nicht dessen Wert. Ich weiss, dass dies ein
etwas schwieriges Thema ist, dashalb wird in den Übungsaufgaben noch
einmal gründlich darauf eingegangen.
Zunächst einmal zurück zur
(if)-Funktion. Lassen Sie uns ein
paar Beispiele mit den jetzt bekannten Testfunktionen begutachten:
(if(> zahl 100) 100 zahl)
Dieser Ausdruck gibt uns
zahl zurück, wenn
zahl nicht
grösser als 100 ist. Ist dies jedoch der Fall, wird 100 zurückgegeben.
Der Ausdruck begrenzt also einen Zahlenwert auf maximal 100. Werfen Sie
nun einen Blick auf das nächste Beispiel:
(if(< zahl 100) zahl nil)
Dieser Ausdruck gibt dann
nil zurück, wenn
zahl grösser
als 100 ist. Bei diesem Beispiel gibt es aber noch eine Feinheit zu
beobachten. Wir können den Ausdruck verkürzen zu
(if(< zahl 100) zahl)
Lassen Sie sich nicht zu der Annahme verleiten, dass dieser Ausdruck
nichts zurückgibt, wenn
zahl grösser als 100 ist. Jeder Ausdruck
in LISP hat eine Rückgabe! Hier wird im Fall, dass
zahl nicht
zurückgegeben wird, das Ergebnis der Evaluation der Funktion
(< ...) zurückgegeben, und das ist eben
nil. Durch
die vorgenommene Verkürzung des Ausdrucks ändert sich also nichts!
Noch ein paar Beispiele:
(defun 3d-punkt(punkt)
(if(=(length punkt)2)
(append punkt(list 0.0))
punkt
)
)
Diese Funktion gibt immer einen vollständigen 3D-Punkt mit Z=0 zurück.
(if
(>=(- zahl(fix zahl))0.5)
(1+(fix zahl))
(fix zahl)
)
Dieser Ausdruck rundet eine Realzahl auf oder ab.
(if(member dieses-symbol jene-liste)
dieses-symbol
)
Dieser Ausdruck verändert die (member)-Rückgabe so, dass nicht mehr
der Rest der Liste zurückgegeben wird, sondern nur das gesuchte Symbol.
Aber ganz am Rande: Das kann man auch mit
(car(member dieses-symbol jene-liste))
erreichen, da
(car nil) ja auch nil ergibt.
Übungsaufgaben
-
Welche Testfunktionen sind im Ergebnis gleich mit den Konstruktionen
(not(> ...))
(not(= ...))
(and(not ...)(not ...))
-