Kurzübersicht über Haskell Grundtypen und Funktionen Grundtypen sind Int für Ganzzahlen und Bool für die Wahrheitswerte True und False. Die Exponentiation über Int wird als x^n geschrieben. Konjunktion bzw. Disjunktion über Bool lauten && bzw. ||, wie in C. Sie werten ihre Argumente von links nach rechts aus, d.h. es gilt x && y x || y = = if x then y else False if x then True else y Der Typ der Funktionen mit Argumenten vom Typ a und Ergebnissen vom Typ b ist a -> b. Daß eine Funktion f diesen Typ hat, wird durch f :: a -> b ausgedrückt. Für eine Funktion f :: a -> b und einen Argumentausdruck e :: a bezeichnet der Ausdruck f E die Anwendung von f auf E (wenigstens ein Zwischeraum als Trennzeichen ist nötig). Funktionen mit mehreren Argumenten werden meistens in gestufter (kaskadierter) Form f x1 x2 ... xn verwendet. In diesem Fall hat die Funktion f den höherstufigen Typ f :: t1 -> (t2 -> ... (tn -> t) ...) oder, abgekürzt, f :: t1 -> t2 -> ... tn -> t (der Pfeil -> bindet nach rechts, wohingegen Funktionsanwendung nach links bindet). Funktionen werden definiert durch Gleichungen f x = E oder als (anonyme) Funktionen \x -> E. So ist \x -> x+1 die Nachfolgerfunktion. Zweistellige Funktionen f :: a -> b -> c können auch als Infix-Operatoren in der Form x ‘f‘ y verwendet werden; dies ist äquivalent zu der gewöhnlichen Anwendung f x y. Z.B. kann man statt div x y auch x ‘div‘ y schreiben. Achtung: Hier darf nicht der gewöhnliche Apostroph ’ verwendet werden; es muß der accent grave ‘ sein (es empfiehlt sich nach dessen Eingabe gleich die Leertaste zu drücken, da sonst u.U. der accent über das nachfolgende Zeichen rutscht, statt davor zu stehen). Übergibt man einem zweistelligen Operator ? nur eines seiner Argumente, so erhält man eine Restfunktion oder einen Operatorabschnitt der Form (x ?) (? y) = = \y -> x ? y \x -> x ? y Beispiel. (+1) und (1+) sind jeweils wieder die Nachfolgerfunktion. Fallunterscheidung und Zusicherungen Haskell bietet mehrere Möglichkeiten der Fallunterscheidung, darunter das gewöhnliche if then else. Um Kaskaden von ifs zu vermeiden, kann eine Funktion im Stil einer mathematischen Fallunterscheidung definiert werden. Die Notation lautet f x | C1 ... | Cn = E1 = En Ergebnis ist der Wert des ersten Ausdrucks Ei, für den das zugehörige Ci den Wert True hat. Gibt es kein solches Ci, ist das Ergebnis undefiniert. Zur Vermeidung solcher Partialitäten verwendet man die vordefinierte Konstante otherwise = True und einen Schlußfall | otherwise = En+1 Eine weitere Möglichkeit für Fallunterscheidungen bieten Muster (Patterns). Mehrere Definitionsgleichungen legen das Verhalten der betreffenden Funktion für Argumente unterschiedlicher Gestalt (etwa leere/nichtleere Liste) fest. Die Gleichungen werden in der Reihenfolge der textuellen Aufschreibung versucht; die erste, deren Muster auf das aktuelle Argument paßt, wird angewendet. Paßt kein Muster, ist die Funktion für diese Argument undefiniert. Beispiel. Durch die Gleichungen f 0 = 5 f 1 = 7 wird die Funktion f :: Int -> Int nur für die Argumentwerte 0 und 1 definiert. Listen Der Typ der Listen mit Elements vom Typ a ist [a]. Die Liste mit Elementen x1,...,xn schreibt sich [x1,...,xn]; speziell ist [] die leere Liste. Konkatenation wird mit dem Infixoperator ++ notiert. Das Vornanfügen eines Elements an eine Liste leistet der Operator : x:xs = [x] ++ xs Die Funktion length liefert die Länge, d.h. die Elementzahl einer Liste. Das ite Element der Liste xs wird durch xs!!i selektiert (wobei die Numerierung mit 0 beginnt). Eine Liste kann mit den Funktionen take, drop :: [a] -> Int -> [a] in zwei Teile aufgespalten werden. Für eine nicht-negative Ganzzahl k besteht die Liste take k xs aus den ersten k Elementen von xs wenn k <= length xs, und aus ganz xs wenn k > length xs. Für negative k ist der Wert des Ausdrucks take k xs undefiniert. Die Liste drop k xs entstht durch Streichen des Anteils take k xs am Anfang von xs. Daher gilt (für k >= 0) stets take k xs ++ drop k xs = xs Ein sehr nützliches Sprachmittel ist die Listenkomprehension in der Form [ f x | x <- L, p x] Dabie ist L ein listenwertiger Ausdruck, f eine Funktion auf den Listenelementen und p eine Bool-wertige Funktion. Das Symbol <- kann als links gerichteter Pfeil oder als eckiges Enthaltenseinszeichen ∈ (gesprochen “aus”) gedeutet werden. In der letzteren Sichtweise stellt die Listenkomprehension das Listenanalogon zur gewöhlichen Mengenkomprehension {f x | x ∈ S ∧ p x} dar. Der Wert von [ f x | x <- L, p x ] ist wieder eine Liste, die wie folgt entsteht: • Die Elemente der Liste L werden von links nach rechts durchmustert. • Für jedes Element x wird die Bedingung p geprüft. • Ist p x = True, wird f x in die Ergebnisliste aufgenommen. • Andernfalls wird x ignoriert. Spezialfälle der Listenkomprehension sind Allanwendung map f xs = [ f x | x <- L ] und Filtern mit einem Prädikat filter p xs = [p x | x <- xs ] Die Liste [m,m+1,...,n] von Ganzzahlen kann kurz als [m..n] notiert werden. Wird die rechte Schranke n weggelassen, bedeutet der Ausdruck die unendliche Liste [m, m+1, ... ]. Eine weitere nützliche Funktion auf nichtleeren Listen ist das Verknüpfen ihrer Elemente mit einem zweistelligen Operator f :: a -> a -> a: foldr1 f [x1,...,xn] = f x1 (f x2 ... (f xn-1 xn)...) Die Funktion foldr1 selbst hat den Typ (a -> a -> a) -> [a] -> a. Die Variante foldr von foldr1 kann auch leere Listen bearbeiten; sie verwendet ein zusätzliches Argument e, das den Wert für leere Listen spezifiziert. Die Definitionsgleichungen lauten foldr f e [] = e foldr f e [x] ++ xs = f x (foldr f e xs) Als Beispiel diene die vordefinierte Funktion sum s = foldr (+) 0 s Mittels foldr kann man auch All- und Existenzquantor für Listen definieren. Für ein Prädikat p :: a -> Bool hat man all p xs exist p xs = = foldr (&&) True (filter p xs) foldr (||) False (filter p xs) Damit hat all p xs den Wert True gdw p x für alle x in xs den Wert True hat.