Wir haben im vorigen Kapitel eine Vorstellung von einfachen
if-Verzweigungen entwickelt. Was aber, wenn die Sachverhalte
komplizierter werden? Es könnte ja sein, dass Sie im WENN-Fall
nicht nur einen Ausdruck evaluieren lassen möchten, sondern
gleich mehrere? Nehmen wir folgendes an: Sie untersuchen eine
Liste. Ist ihre Länge grösser als 10, soll die Variable
zu-lange-liste auf 'T gesetzt werden und nur das erste Element
zurückgegeben werden. Im SONST-Fall soll die Liste unverändert
zurückgegeben werden. Der gutgemeinte folgende Versuch
funktioniert leider nicht, sondern endet mit einer Fehlermeldung:
(if (> (length liste) 10) ; Fehler!!!
((setq zu-lange-liste 'T)(car liste))
liste
)
Dieser Versuch, mit einer umgebenden Klammer zwei Ausdrücke
zusammenzufassen, ist zum Scheitern verurteilt, da - wenn der
Ausdruck evaluiert wird - das erste Element kein Funktionsaufruf
ist. Wenn wir mehrere Ausdrücke zusammenfassen wollen, müssen
wir uns der Funktion
(progn) bedienen, die folgendes leistet:
(progn) fasst beliebige Ausdrücke zu einem einzigen
Ausdruck zusammen und sorgt dafür, daá sie von links nach
rechts abgearbeitet werden.
(progn) quotiert übrigens alle seine Argumente automatisch,
so dass Sie erst beim Aufruf von
(progn) evaluiert werden,
aber nicht schon bei der Übergabe an
(progn). Wir können
jetzt, da wir sowieso schon dabei sind, auch einmal klären, wie
sich
(if) in dieser Hinsicht verhält: Die Evaluation des
zweiten und des dritten Argumentes wird verhindert, d.h. die
Evaluation findet erst dann statt, wenn die
if-Entscheidung
getroffen wurde.
Wir müssen also unser letztes Beispiel noch einmal abändern, damit
es funktioniert:
(if (> (length liste) 10)
(progn
(setq zu-lange-liste 'T)
(car liste)
)
liste
)
if-Ausdrücke können aber auch dadurch komplizierter werden,
dass mehrere Bedingungen gleichzeitig getestet werden sollen: Sie
könnten z.B. eine Funktion brauchen, die testet, ob ein Punkt
innerhalb eines bestimmten (orthogonalen) Rechteckes angesiedelt
ist, und dann die Variable punkt-ist-ok auf den entsprechenden Wert
(
'T oder
nil) setzt. Um mehrere Testbedingungen
zu verknüpfen, stehen uns die Logik-Funktionen
(and),
(or) und
(not) zur Verfügung.
(and) und
(or) geben immer entweder
T oder
nil zurück, es gibt aber einen wichtigen Unterschied:
(and ...) gibt dann
T zurück, wenn alle seine
Argumente wahr sind. Es müssen also alle Argumente evaluiert
werden.
(or ...) arbeitet anders: Es gibt dann
T
zurück, wenn eines seiner Argumente wahr ist. Daher werden bei
(or) die Argumente ebenfalls von links nach rechts
abgearbeitet, aber nicht unbedingt alle: Sobald eines gefunden
wurde, das nicht
nil ergibt, stellt
(or ...) die
Arbeit ein und evaluiert nicht mehr weiter.
(not ...) kann immer nur ein einziges Argument bekommen.
Ist dieses whar, wird
nil zurückgegeben, ist es
nil,
wird
'T zurückgegeben.
Mit
(and ...)
können wir unsere gestellte Aufgabe erst einmal so lösen:
(if
(and
(< x rechte-grenze)
(> x linke-grenze)
(< y obere-grenze)
(> y untere-grenze)
)
(setq punkt-ist-ok 'T)
(setq punkt-ist-ok nil)
)
Beim näheren Hinsehen lässt sich feststellen, dass auch diese
Lösung sich noch verkürzen lässt:
(setq punkt-ist-ok
(and
(< x rechte-grenze)
(> x linke-grenze)
(< y obere-grenze)
(> y untere-grenze)
)
)
Sie sehen, dass die
(if)-Anweisung, obwohl gerade erst
gelernt, sich hier als überflüssig erweist. Wir kommen da zu
einem wichtigen Punkt. Man sollte nicht nur wissen, wie man
(if) anwendet, sondern auch, wann man es besser weglässt.
Der Prototyp der überflüssigen
(if)-Anweisung lautet:
(if irgend-ein-ausdruck 'T nil)
Diese Formulierung ist immer dann überflüssig, wenn
irgend-ein-ausdruck auch nur
T oder
nil zurückgibt,
also eine Testfunktion ist. Eine solche
(if)-Konstruktion
plappert also wie ein Papagei nur den Effekt des Testausdrucks
nach. Sinnvoll kann diese Konstruktion nur sein, wenn der
Testausdruck etwas anderes als T zurückgibt (wie z.B.
(member)).
Aber auch dann kann man meist darauf verzichten, da ja alles, was
nicht
nil ist, wahr ist. Schauen Sie sich noch einmal
folgendes Programmfragment an:
(if(member dieses-element jene-liste)
(tu-dieses ...)
(tu-jenes ...)
)
(member ...) gibt ja nicht das Symbol
T zurück,
sondern eine Liste, die mit
dieses-element beginnt, falls
es in
jene-liste enthalten ist. Wenn das nicht der Fall
ist, kommt eben die leere Liste (=
nil) zurück. Diese
Konstruktion macht auch besonders deutlich, warum
nil die
Abkürzung für 'Not In List' ist.
Übungsaufgaben
-
Etwas weiter oben hatten wir den Ausdruck
(setq punkt-ist-ok ...), der bereits eine verkürzte
Form der Vorgängerversion ist. Denken Sie daran, was Sie
über die Vergleichsfunktionen (< ...) und
(> ...) gelernt haben und verkürzen Sie den
Ausdruck um weitere zwei Zeilen. Sie benötigen dann nur noch
eine der beiden Vergleichsfunktionen.
-
Definieren Sie eine Funktion, die testet, ob ein
2D-Punkt im rechten oberen Quadranten des Koordinatensystems
liegt. Machen Sie die Funktion so kurz wie möglich.
-
Definieren Sie eine Funktion, die testet, ob ein
2D-Punkt innerhalb eines Kreises um den Mittelpunkt
M liegt. Wenn der Punkt genau auf der Kreislinie liegt,
gilt er noch als innerhalb. Benutzen Sie dazu die
Funktion (distance p1 p2), die den Abstand
zwischen zwei Punkten berechnet.
-
Lösen Sie die vorige Aufgabe noch einmal, ohne
(distance) zu verwenden, d.h. Sie benötigen
den Satz des Pythagoras.