In den letzten Kapiteln haben wir gelernt, wie man den
Programmablauf anhand von Bedingungen verzweigen, d.h. in
bestimmte Richtungen steuern kann. Wir werden uns nun der
Programmsteuerung durch Schleifen zuwenden. Diese können
aus ganz unterschiedlichen Gründen notwendig werden, z.B.:
'Tue dieses so lange, bis jener Ereignis eintritt'. Das könnte
der Fall sein, dass man so lange Linien vom Bildschirm löscht,
bis keine Linien mehr vorhanden sind.
'Wiederhole dieses so lange, wie eine Bedingung zutrifft'
könnte sein, dass eine Anfrage an den Programmbenutzer so
lange wiederholt wird, wie dieser eine ungültige Antwort
eingibt. 'Tue dieses x mal' ist schliesslich eine unkonditionierte
Schleife, die einfach x mal durchlaufen werden soll.
Fangen wir mit der letzten Möglichkeit an, da sie die
einfachste ist. Die unkonditionierte (bedingungslose)
Wiederholung von Programmabschnitten wird durch
(repeat)
erzeugt.
(repeat) erhält zwei bis beliebig viele Argumente,
von denen alle ab dem zweiten automatisch quotiert werden.
(repeat 100 (princ "\nIch bin eine repeat-Schleife"))
gibt diesen Text 100 mal am Bildschirm aus - ohne wenn und aber.
Das Zeichen '\n' ist übrigens ein Steuerzeichen, das einen
Zeilenumbruch verursacht. Jede der 100 Ausgaben erfolgt also
in einer neuen Zeile. Wenn wir
(repeat) mit mehr als zwei
Argumenten aufrufen, werden alle Argumente ab dem zweiten x mal
nacheinander abgearbeitet, wir brauchen also nichts mit
(progn) zusammenzufassen, wie es bei
(if) der Fall
ist.
Es gibt keine Möglichkeit, eine
(repeat)-Schleife vorzeitig
zu verlassen. Analysieren wir dazu ein Beispiel:
(setq x 100)
(repeat x (setq x 0))
Auch diese Schleife wird 100 mal durchlaufen. Das liegt daran,
dass das erste Argument von
(repeat) schon beim Aufruf
evaluiert wird. Nur die Argumente ab Nr. 2 werden ja automatisch
quotiert. Daher erhält die
(repeat)-Funktion ja nicht das
Symbol x als Argument, sondern die Zahl 100. Man könnte es überspitzt
so formulieren, dass die
(repeat)-Funktion gar keine Ahnung
haben kann, dass die Variable x etwas mit ihr zu tun hat. Sie kann
also gar nicht nachträglich auf irgendwelche Änderungen in x
reagieren.
Da es kaum eine einfachere Funktion als
(repeat ...) in Lisp gibt,
wenden wir uns gleich der nächsten Funktion zur Schleifensteuerung
zu. Die Funktion
(while ...) stellt das Modell 'Tu dies, solange
jene Bedingung erfüllt ist' dar und ist eigentlich auch nicht viel
komplizierter als
(repeat). Das erste Argument für
(while)
ist eine Testbedingung, wie wir sie von
(if) und
(cond)
her kennen. Anschliessend folgen wie bei
(repeat) beliebig
viele Ausdrücke, die bei jedem Durchlauf der Schleife nacheinander
evaluiert werden.
Natürlich wird bei
(while) die Testbedingung vor jedem Durchlauf
erneut evaluiert. Sonst könnte man ja eine
(while)-Schleife nie
wieder verlassen. Aus diesen Überlegungungen können wir schliessen, dass
(while) im Gegensatz zu
(repeat) auch das erste Argument
automatisch quotiert. Das muss so sein, da sonst eine immer wiederkehrende
Evaluation vor den Durchläufen gar nicht möglich wäre.
Ein ganz einfaches Beispiel:
(setq zahl 1)
(while (<= zahl 100)
(setq zahl(1+ zahl))
)
Diese Schleife wird 99 mal durchlaufen, nicht 100 mal! Nachdem
zahl auf den Wert 100 gesetzt wurde, wird die
Schleifenwiederholung abgebrochen.
Im Gegensatz zur
(repeat)-Schleife kann man eine
(while)-Schleife aber auch vorzeitig abbrechen. Dies geht
allerdings nicht auf direktem Wege, sondern nur über den Umweg
des Testausdrucks. Gründe für einen vorzeitigen Schleifenabbruch
können ganz verschieden sein. Stellen Sie sich vor, eine Funktion
soll 20 Elemente aus einer Liste extrahieren, verarbeiten und in
einer anderen Liste speichern. Nach dem 9. Element ist die
Ausgangsliste aber plötzlich schon zu Ende.
Oder eine Anfrageroutine wird solange wiederholt, bis der Benutzer
einen gültigen Wert eingegeben hat. Da der Benutzer aber vielleicht
irgendetwas völlig missverstanden hat, gibt er immer wieder
ungültige Werte ein. Das Programm sollte also in der Lage sein, nach
drei oder fünf Fehlversuchen abzubrechen und eine andere Aktion
auszulösen, z.B. den Benutzer noch einmal aufzuklären, was hier
für ein Wert gefragt ist.
Eine solche
(while)-Schleife mit Zusatz-Kondition wird man
anlegen, indem man die Hauptbedingung und die Zusatzbedingung per
(and) miteinander verknüpft.
(setq weitermachen 'T)
(while (and <hauptbedingung> weitermachen) ...)
Für
<hauptbedingung> können Sie hier jede beliebige
Testfunktion einsetzen.
Es gibt noch eine dritte Möglichkeit der Schleifensteuerung, dies
ist die Funktion (foreach). Da dies aber sowohl eine Funktion
zur Listenbearbeitung als auch eine Schleifenfunktion ist, werden
wir sie erst in den Kapiteln zu den komplexeren Funktionen für
die Listenbearbeitung behandeln.
Übungsaufgaben
-
Schreiben Sie eine Funktion (multiplik z1 z2),
die zwei Integerzahlen mulipliziert, dabei aber nicht auf
(*) zurückgreift, sondern nur auf (+). Die
Multiplikation wird also durch Additionen ersetzt.
-
Schreiben Sie eine Funktion (subtrak), die eine Integerzahl
von einer anderen Zahl (int oder real) abzieht und dabei nur
auf (1-) zurückgreift.