Verzweigungen in Clojure
Dieses Posting setzt unsere Clojure-Einführung (hier Teil 1, Teil 2) fort. Dieses Mal geht es um Fallunterscheidungen.
Zur Erinnerung: In Teil 1 steht, wie Sie eine einfache IDE installieren und betreiben können.
Nehmen wir, an, wir wollten für Verkehrssünder aus dem Flensburg-Punktestand berechnen, welche Maßnahmen daraus folgen. Dazu schreiben wir das Gerüst einer Funktion:
Hier wird gleich ein weiteres Clojure-Feature deutlich, nämlich die Möglichkeit, einen Docstring für die Funktion mit einer kurzen Beschreibung bereitzustellen. Nun müssen wir irgendwie zwischen den vier verschiedenen Stufen des „Punkte-Tachos“ unterscheiden, der so aussieht:
1 bis 3 Punkte | Vormerkung |
4 bis 5 Punkte | Ermahnung |
6 bis 7 Punkt | Verwarnung |
8 Punkte | Entziehung der Fahrerlaubnis |
Die Tests dafür sehen so aus:
Nun müssen wir diese Tests benutzen, um eine Verzweigung
vorzunehmen, also abhängig davon, welcher Test zutrifft, die
entsprechende Maßnahme zuordnen. In Clojure geht das mit
cond
(für „Conditional“) und sieht so aus:
In einer cond
-Form befinden sich abwechselnd Tests und Ausdrücke.
(Die Kombination von Test und Ausdruck heißt Zweig.)
Bei der Auswertung des cond
werden nacheinander alle Tests
ausgewertet, bis einer zutrifft - in dem Fall wird dann der
zugehörige Ausdruck zum Wert der cond
-Form gemacht.
Es fehlen im Beispiel noch die Ausdrücke, welche die jeweiligen Maßnahmen liefern. Die verschiedenen Maßnahmen bilden eine Aufzählung, also eine feste Menge von Möglichkeiten. In Clojure kommen für Aufzählungen Keywords zum Einsatz. Das sieht dann so aus:
(Wer Scheme oder einen anderen Lisp-Dialekt kennt, muss sich merken, dass in Clojure nicht jeweils ein Klammernpaar um jeden Zweig steht.)
Aufpassen muss man, wenn es möglich ist, dass keiner der Tests zutrifft. Zum Beispiel:
Um dies zu verhindern, können wir dem cond
einen Zweig hinzufügen,
der immer zutrifft, wenn alle anderen Tests fehlschlagen.
Prinzipiell ist es möglich, einfach true
als Test zu verwenden:
In Clojure ist allerdings Konvention, stattdessen das Keyword :else
zu verwenden:
Das funktioniert deshalb, weil in Clojure jeder Wert, der nicht
false
oder nil
ist, als „logisch wahr“ gilt.
Oft ist eine Verzweigung binär, hat also nur einen „richtigen“
Test und einen :else
-Zweig, wie etwa diese Funktion:
Binäre Verzweigungen können wir etwas kompakter mit
if
schreiben:
Der Umstand, dass alles außer false
und nil
als „wahr“ gilt,
machen sich Clojure-Programme oft zunutze, zum Beispiel beim Zugriff
auf Maps. Nehmen wir an, eine Funktion solle eine Adresse in einer Map
nachschlagen und, falls diese nicht vorhanden ist, :unbekannt
zurückliefern:
Die
if-let
-Form
akzeptiert eine Bindung der Form [<name> <exp>]
als ersten
Operand, wobei <name>
ein Name und <exp>
ein Ausdruck ist. Wenn
der Ausdruck einen wahren Wert liefert, wird er an <name>
gebunden
und der erste Zweig der if-let
-Form gebunden - ansonsten der zweite
Zweig.
Auch dieses Beispiel können wir noch kürzer schreiben, weil
or
nicht einfach true
liefert, wenn ein Operand „wahr“ ergibt, sondern
stattdessen den Wert des ersten Operanden, der „wahr“ ergibt:
Soweit so gut für heute!
Weiter geht‘s mit zusammengesetzten Daten in Teil 4.