2. Elm
2.1 Elm als Taschenrechner: das Repl-Fenster
Repl ist eine Abkürzung und steht für Read, Evaluate, Print, Loop. Genauer:- Read. Das Fenster wartet darauf, dass der Benutzer einen Ausdruck (zum
Beispiel
(3 + (4 * 5))^2
) eingibt. - Evaluate. Elm evaluiert den Ausdruck, wertet ihn also aus.
- Print. Es druckt das Ergebnis auf der Konsole aus.
- Loop. Es läuft in einer Dauerschleife, springt also wieder zu Read zurück, bis wir es beenden.
Wir werden nun die Dinge aus dem vorherigen Kapitel im Repl-Fenster von Elm schreiben. In diesem Abschnitt machen einen kurzen Schnelldurchgang. Danach werden wir alles noch einmal genauer unter die Lupe nehmen.
Ausdrücke und Identifier
Öffnen Sie eine Konsole und geben Sieelm repl
ein. Dann tippen Sie
(3 + (4 * 5))^2
und sehen, was geschieht. Sie können das Repl-Fenster also wie einen Taschenrechner verwenden.
Sehen wir als nächstes, wie man Bezeichner (Identifier) einführt. Geben Sie ein:
stundenProTag = 24
minutenProStunde = 60
sekundenProMinute = 60
stundenProTag * minutenProStunde * sekundenProMinute
Hier werden mehrere Bezeichner eingeführt. Der String stundenProTag
hat ab sofort
einen Wert,
und zwar 24
. Er kann von nun an wie eine Zahl in jedem Kontext verwendet werden.
Funktionen
Erinnern Sie sich, wie ich in Kapitel 1.2 informell Funktionen definiert habe, zum Beispielf(n) = n * (n+1) / 2
. In Elm geht das ähnlich, allerdings ohne die in
der Mathematik
üblichen Klammern:
f n = n * (n+1) / 2
Auch hier wird ein neuer Bezeichner eingeführt: f
. Allerdings erkennt Elm, dass vor
dem =
noch etwas kommt,
nämlich das n
. Elm weiß nun also, dass es sich bei f
um eine Funktion
mit
Parameter n
und Funktionskörper n * (n+1) / 2
handelt.
Wir können f
jetzt aufrufen:
f 5
15
Beachten Sie, dass wir in Elm nicht f (n)
sondern f n
schreiben. Die
Argumente werden einfach
rechts vom Funktionsnamen geschrieben, mit Leerzeichen separiert, ohne Komma.
Natürlich können wir so einen Funktionsaufruf wiederum in komplexeren Ausdrücken verwenden:
f 5 * f 6
f 5 * 3
? Da gibt es zwei Möglichkeiten:
- Es wird erst
5 * 3
ausgewertet und das Ergebnis dann der Funktionf
als Eingabe-Argument übergeben. - Es wird erst
f 5
ausgewertet und dann das Ergebnis mit3
multipliziert.
Operatorenpräzedenz
In der Schule haben Sie wahrscheinlich die Regel Punkt
vor Strich kennengelernt.
Diese besagt, dass
\begin{align*}
3 + 4 \cdot 5
\end{align*}
eben als \(3 + (4 \cdot 5)\) und nicht als \((3 + 4) \cdot 5\) zu interpretieren ist. Das
\(\cdot\) klebt stärker
als das \(+\). Hierbei handelt es sich nicht um ein Naturgesetz, sondern einfach um eine
Konvention, um
Klammern einzusparen. Funktionsaufrufe in Elm "kleben" nun noch stärker als \(\cdot\), so dass
eben
f 5 * 3
als
(f 5) * 3
interpretiert wird. Falls Sie aber f (5 * 3)
meinen, also
wollen, dass zuerst 5 * 3
ausgewertet wird, dann müssen Sie diese Klammern auch
setzen,
also eben auch f (5 * 3)
schreiben.
Funktionen können natürlich auch mehrere Parameter haben. Betrachten Sie \begin{align*} g(x,y) = x^2 + y^2 \ . \end{align*} In Elm können wir \(g\) wie folgt definieren:
g x y = x^2 + y^2
und aufrufen:
g 3 4
25 : number
Wir haben nun also zwei Funktionen definiert: die einstellige Funktion \(f\) und die zweistellige Funktion \(g\). Wie können wir nun Dinge wie \(g(2, f(5))\) in Elm schreiben? Probieren Sie es aus:
g 2 f 5
Sie sehen nun eine Fehlermeldung. Der entscheidende Satz ist:
The `g` function expects 2 arguments, but it got 3 instead.
Sehen Sie,
Elm interpretiert den Ausdruck g 2 f 5
als Aufruf der Funktion g
mit
drei Argumenten, nämlich 2
, f
und 5
. Die Funktion
g
nimmt
aber nur zwei Argumente. Daher Fehlermeldung. Wenn Sie also
\(g(2, f(5))\) meinen, dann müssen Sie das entsprechend klamern:
g 2 (f 5)
Nun sieht Elm den Funktionsnamen g
mit zwei Argumenten: 2
und
f 5
, und alles
ist gut. Intern geschieht mit diesem Ausdruck also folgendes:
g 2 (f 5)
g 2 (5 * (5 + 1) / 2) g 2 (5 * 6 / 2) g 2 (30 / 2) g 2 15 2^2 + 15^2 4 + 225229
Fallunterscheidungen
In Kapitel 1.3 habe ich über Fallunterscheidungen gesprochen. Einführendes Beispiel war die Absolutwertfunktion, die ich alsabs(x) = if x >= 0 then x else -x
geschrieben habe.
In Elm geht das ganz genau so:
absVal x = if x >= 0 then x else -x
Ich habe hier absVal
statt abs
geschrieben, weil Elm die Funktion
abs
bereits
"von Haus aus" kennt. Damit also keine Verwechslungen auftreten. Die
if-then-else
-Konstruktion kann natürlich
auch verschachtelt werden; das heißt: hinter dem else
darf wiederum eine
Fallunterscheidung stehen:
h x = if x <= -1 then -1 else (if x <= 1 then x else 1)
In diesem Falle dürfen wir die Klammern auch weglassen, weil es keine Verwechslungsgefahr gibt.
Strings
Als wichtigen Datentyp neben Zahlen haben wir im Kapitel 1.4 die Strings kennengelernt. In Elm gibt es für Strings bereits viele vorgefertigte Funktionen. Tippen Sie mir nach:wort = "Bekanntmachung"
String.right 3 wort
"ung" : String
String.left 4 wort
"Beka" : String
wort ++ "en"
"Bekanntmachungen" : String
String.length wort
14 : Int
Versuchen wir jetzt, unsere einfache "Präteritum"-Funktion zu schreiben, die die letzten
beiden Buchstaben entfernt und durch "te"
ersetzt:
removeRight word k = String.left (String.length word - k) word
removeRight "kochen" 2
"koch"
Diese Funktion berechnet die Wortlänge, hier \(n\) genannt, zieht davon \(k\) ab, und nimmt also die ersten \(n-k\) Zeichen des Wortes.
insPraeteritum word = removeRight word 2 ++ "te"
insPraeteritum "kochen"
"kochte" : String
insPraeteritum
mit Fallunterscheidungen, damit es für
Verben wie
schalten, walten, baden, angeln, krabbeln, knabbern korrekt funktioniert.
Tip. Mit String.right 2 wort
erhalten Sie die
letzten zwei Buchstaben des Wortes. Sie können dann beispielsweise mit
if String.right 2 wort == "ln"
testen, ob das Wort auf "ln" endet.