Ausdrücke auswerten Ausdrücke über Ganzzahlen Im Haskell-Interpreter können Ausdrücke direkt ausgewertet werden: Prelude> 42 42 Prelude> 5*3 15 • Konstanten: 1, 3, 42, 321432343243245 • Arithmetische Ausdrücke: 1+2, 4*2, 5+2*7, - 4 • Funktionsaufrufe: cos 2 Interpreter gibt Repräsentation des Wertes aus Definitionen Eine Definition ist eine Gleichung: • name = expr z.B. pi = 3.14 Macht name im ganzen Programm verfügbar Progamm = Reihe von Definitionen Definitionen laden Nach dem Start des Interpreters sind die Definitionen aus der Prelude verfügbar Weitere Definitionen müssen geladen werden: :load filename Haskell-Dateien sollten die Endung .hs haben Inhalt der Dateien: Reihe von Gleichungen 1-4 Bezeichner in Haskell Funktionsdefinition Mathematik: f(x) = cos(x) + 2 • Erstes Zeichen: Kleinbuchstabe Haskell: name var ... = expr • Dann Buchstaben, Zahlen und ' z.B. • Bsp: abs, abs', splitAt, writeFile, zip3 Funktionsdefinition durch Fallaufzählung f x = cos x + 2 square x = x * x minus x y = x - y Auswertung von Definitionen: Vereinfachen 1+1=2 1+2=3 2+3=5 Wenn die rechte Seite einer Definition auf einen Ausdruck passt, ersetze den Ausdruck durch die linke Seite der Definition. In Haskell: add 1 1 = 2 add 1 2 = 3 add 2 5 = 7 Auch dabei Variablen möglich: 1 + 1 = 2 add 2 5 = 7 1 + (2 * pi) = 1 + 6.28 add' 0 x = x add' 1 1 = 2 add' 1 2 = 3 Bei Überlappung: Erster von oben 5-8 Auswertung von Funktionsaufrufen: Einsetzen Enthält die rechte Seite Variablen, so ersetze die Variablen durch die entsprechenden Argumente Normalform f (4 * 3) = cos (4 * 3) + 2 Kann ein Ausdruck nicht mehr weiter ausgewertet werden, so ist er in Normalform. 1 + (square (1 + 2)) = 1 + ((1 + 2) * (1 + 2)) Bsp: 12, 3.14, minus minus (1 * 1) (4 / 2) = (1 * 1) - (4 / 2) Auswertungsreihenfolge Bsp: Alternative Auswertungsreihenfolgen square (3 + 4) = square 7 = 7*7 = 49 Wichtige Eigenschaft der Funktionalen Programmierung: Wenn zwei Auswertungsfolgen terminieren, so liefern sie dieselbe Normalform. square (3 + 4) = (3 + 4) * (3 + 4) = 7 * (3 + 4) = 7 * 7 = 49 square (3 + 4) = (3 + 4) * (3 + 4) = (3 + 4) * 7 = 7 * 7 = 49 9-12 Auswertungsstrategie Werte Eine Auswertungsstrategie legt eine bestimmte Auswertungsreihenfolge fest. Ein Ausdruck beschreibt einen Wert Beispiele: Call-by-Name, Call-by-Value Mögliche Arten von Werte: Zahlen, Boolean, Zeichen, Tupel, Listen, Funktionen, ... Später: Lazy-Evaluation als Strategie für Haskell Kanonische Repräsentation Bottom Werte sind abstrakt, Ausdrücke repräsentieren Werte Manche Ausdrücke repräsentieren keine wohldefinierten Werte Haskell-Interpreter gibt für einen Wert seine kanonische Repräsentation aus Programm: infinity = infinity + 1 Manche Werte haben keine kanonische Rep., z.B. Funktionen oder π infinity 1 / 0 Spezieller Wert: ⊥ 13-16 Striktheit Rekursive Definitionen Wenn f ⊥ = ⊥, dann ist f eine strikte Funktion, sonst ist f eine nicht-strikte Funktion Lazy-Evaluation erlaubt es, nicht-strikte Funktion zu definieren: three x = 3 three infinity = 3 fact n = if n == 0 then 1 else n * fact (n - 1)) Auswertung mit Vereinfachen und Einsetzen: fact 1 = if 1 == 0 then 1 else 1 * fact (1 - 1) = 1 * fact (1 - 1) = 1 * (if (1 - 1) == 0 then 1 else (1 - 1) * fact ((1 - 1) = 1 * (if (0 == 0) then 1 else ... = 1 * 1 = 1 Typen Typsignaturen Typ = Menge von Werten Typ für Zahlen: Integer Typ für Funktionen: t1 ->... tN -> tR Wobei t1... tN die Typen der Argumente und tR der Typ des Ergebnisses sind. Bsp: Typ von add: Integer -> Integer -> Integer Bei einer Definition kann der Typ mit angegeben werden = Typsignatur. Syntax: name :: typ Bsp: square :: Integer -> Integer square x = x * x Kommando :type expr liefert Typ des Ausdrucks expr 17-20 Vorteile von Typsignaturen • Teil des Designs: Man macht sich erste Gedanken über Ein- und Ausgabe • Bessere Fehlermeldungen, denn der Compiler liest die Signaturen zuerst Funktionen sind einstellig Funktionen in Haskell sind in Wahrheit einstellig: f x y = e definiert eine einstellige Funktion, die eine einstellige Funktion zurückgibt: fTwo = f 2 hat Typ Integer ->> Integer • Teil der Dokumentation: Leser kennt sofort den Typ der Ein/Ausgaben ⇒ Bei den Lösungen der Übungsaufgaben immer angeben Funktionstypen -> ist rechtsassoziativ Integer -> Integer -> Integer ist gleich Integer -> (Integer -> Integer) fTwo 3 ≡ (f 2) 3 ≡ f 2 3 Currying Currying erleichtert Wiederverwendung twice :: (Integer -> Integer) -> Integer -> Integer twice f x = f (f x) quad :: Integer -> Integer quad = twice square 21-24 Boolsche Werte Boolsche Werte Operatoren: &&, ||, not, == Eigener Typ: Bool exOr :: Bool -> Bool -> Bool exOr x y = (x || y) && not (x && y) Werte des Typs: • Wahr True Alternative Definition: • Falsch False exOr True x = not x exOr False x = x Bedingte Ausdrücke Auswertung für bedingte Ausdrücke Syntax: if test-expr then cons-expr else alt-expr Wenn test-expr True ist, ersetze den if-Ausdruck durch cons-expr, wenn test-expr False ist, ersetze den if-Ausdruck durch alt-expr. • test-expr muss vom Typ Boolean sein Bsp: 1 + (if True 4 else 42) = 1 + 4 = 5 Ganz normales if • cons-expr und alt-expr müssen vom gleichen Typ sein neg :: Bool -> Bool neg test = if test False else True 25-28 Operatoren Def: Ein Operator ist eine Funktion, die infix aufgerufen wird In Haskell wird jede binäre Funktion durch Backquotes zum Operator: Bsp: 3 `smallerc` 4 entspricht smallerc 3 4 Auch in Definition möglich Umgekehrt wird jeder Operator in Klammern zur binären Funktion: Bsp: (+) 3 4 entspricht 3 + 4 Sektionen Sektion: Binärer Operator auf nur ein Argument angewendet. Ergebnis ist eine Funktion: (* (> (1 (+ 2) 0) /) 1) ----- Verdoppeln Test auf positive Zahl Kehrwert Nachfolger Aber: (- 2) ist unäre Negation Funktionskomposition In der Mathematik: f ⋅ g(x) = f(g(x)) In Haskell: Operator . Es gilt also (f . g) x ist gleich f (g x) 29-31