Funktionale Programmierung ALP I Einführung in Haskell WS 2012/2013 Prof. Dr. Margarita Esponda Prof. Dr. Margarita Esponda Funktionale Programmierung Was ist Haskell? Haskell ist eine rein Funktionale Programmiersprache mit einer nach Bedarf Auswertung-Strategie oder "Lazy Evaluation". Prof. Dr. Margarita Esponda Funktionale Programmierung Was bedeutet rein funktional? - Programme werden als mathematische Funktionen dargestellt - Haskell Funktionen haben keine Seiteneffekte. keine Seiteneffekte ⇒ Referenzielle Transparenz - Eine Funktion liefert immer bei gleicher Eingabe das gleiche Ergebnis. - Der Wert eines Ausdrucks hängt nur von den Werten der aktuellen Parameter ab. Prof. Dr. Margarita Esponda Funktionale Programmierung Funktionen Eingabewerte e1 e2 . . en F a nur ein Ausgabewert Das Ergebnis einer Funktion hängt nur von den Eingabewerten ab. Beispiel: f(1,2) 1 2 f ( x , y ) = x 2+ y Prof. Dr. Margarita Esponda 3 Funktionale Programmierung Funktionen Arithmetische Operationen können auch als Funktionen betrachtet werden 1 2 + 3 1 2 (+)(x,y) = x + y Prof. Dr. Margarita Esponda 3 Funktionale Programmierung Funktionen in Haskell Einfache Funktionsdefinitionen in Haskell: f x = x*x quadrat x = x*x Allgemeine Syntax: Ausdruck, dessen Wert dem Ergebnis der Funktion entspricht Funktionsname f e1 e2 ... en = Funktionskörper Eingabeargumente ohne Klammern und Kommas Prof. Dr. Margarita Esponda Funktionale Programmierung Funktionsapplikation Die Anwendung einer Funktion mit konkreten Argumenten wird als Funktionsapplikation bezeichnet. Funktionsdefinition: f x y z = x*y + x*z Funktionsapplikation: f102 ⇒ 1*0 + 1*2 ⇒0+2 ⇒2 Prof. Dr. Margarita Esponda Funktionsreduktion Die Variablen auf der rechten Seite der Funktionsdefinition werden durch die entsprechenden konkreten Argumente ersetzt. Funktionale Programmierung Normalform Kann ein Ausdruck nicht mehr weiter reduziert werden, dann befindet er sich in seiner Normalform und die Berechnung ist beendet. Beispiele: Ausdruck 3+7 sin 0 10 "text" "text" "1 + 2" "1 + 2" '2' '2' Prof. Dr. Margarita Esponda Normalform des Ausdruck 0.0 Zahlen Zeichenkette Zeichen Funktionale Programmierung Auswertungsstrategie ✴ Innerhalb eines Ausdrucks kann es mehrere Funktionsapplikationen geben. ✴ Die Reihenfolge, in der unabhängige Teilausdrücke ausgewertet werden, verändert nicht den Wert des gesamtes Ausdrucks. ✴ Aber verschiedene Reihenfolgen können die Anzahl der notwendigen Berechnungen stark beeinflussen. ✴ Eine Auswertungsstrategie kann sogar entscheiden, ob eine Berechnung beendet werden kann oder nicht. Prof. Dr. Margarita Esponda Funktionale Programmierung Bottom (⊥) ✴ ✴ Wenn die Auswertung eines Ausdrucks zu einer unendlichen Folge von Reduktionen führt, wird entweder • das Programm nicht beendet oder • es stürzt ab, weil der Speicher voll wird. In der Theorie wird das Symbol Bottom ⊥ verwendet, um den Wert von Ausdrücken darzustellen, die nicht vollständig ausgewertet werden können (die divergent sind). Prof. Dr. Margarita Esponda Funktionale Programmierung Bottom (⊥) Bottom kann in Haskell wie folgt ausgedrückt werden: bottom = bottom In dem Prelude-Module ist eine Funktion dafür definiert: undefined = error "Prelude; undefined" Prof. Dr. Margarita Esponda Auswertungsstrategien Auswertungsstrategien Funktionsbeispiele: g x = 2*x f x = x+x Auszuwertender Ausdruck: f (g 5) Call-by-value Call-by-name Ausdrücke werden von innen nach außen und von links nach rechts ausgewertet. Ausdrücke werden von außen nach innen ausgewertet. f (g 5) ⇒ ⇒ ⇒ ⇒ f (2 * 5) f 10 10 + 10 20 Es wird garantiert, dass die Argumente zuerst ausgewertet werden und dann an die Funktion weitergegeben werden. Prof. Dr. Margarita Esponda f (g 5) ⇒ (g 5) + (g 5) ⇒ (2*5) + (g 5) ⇒ 10 + (g 5) ⇒ 10 + (2*5) ⇒ 10 + 10 ⇒ 20 Ohne die Argumente auszuwerten, werden zuerst die äusseren Funktionen angewendet. Auswertungsstrategien Auswertungsstrategie in Haskell Call-by-need oder Lazy-evaluation Auswertung nach Bedarf Lazy-evaluation ist eine optimierte Auswertungsvariante von call-by-name und wird in Haskell und anderen funktionalen Sprachen verwendet. Beispiel: g x = 2*x f x = x*x f (g 5) ⇒ (g 5) * (g 5) where g 5 = 2*5 ⇒ (g 5) * (g 5) where g 5 = 10 ⇒ 10 * 10 ⇒ 100 Prof. Dr. Margarita Esponda call-by-need ist eine Art call-by-name-Auswertungsstrategie mit Gedächtnis Auswertungsstrategien Auswertungsstrategie in Haskell f 6 (double 3) ⇒ (2 + 6) + * Definitionen: f y x = (2 + y) + x * x double x = 2 * x (double 3) ⇒ 8 + * (double 3) Anwendung: f 6 (double 3) ⇒ 8 + * (2*3) Call-by-need oder Lazy-evaluation ⇒ 8 + Auswertung nach Bedarf 6 ⇒ 8 + 36 ⇒ 44 Prof. Dr. Margarita Esponda * Auswertungsstrategien Strikte Funktionen Informell kann man sagen, dass eine Funktion f strikt nach einem ihrer Argumente a ist, wenn für die Auswertung der Funktion, die Auswertung von a notwendig ist. Formale Definition: f ist strikt Beispiel: ⇔ f ⊥ ⊥ Die + Funktion ist strikt nach beiden Argumenten (2*4) + (5*6) oder (+) (2*4) (5*6) ⇒ (2*4) + (5*6) ⇒ 8 + 30 ⇒ 38 Prof. Dr. Margarita Esponda = Auswertungsstrategien Haskell - Auswertungsstrategie ist nicht strikt ✴ ✴ Ausdrücke werden nur bei Bedarf ausgewertet im Gegensatz zu imperative Sprachen, in denen eager-evaluation verwendet wird. Beispiel: x = x+1 g 5 ⇒ 5 g a = a g x ⇒ g⊥ ⇒ ⊥ f a b = b x ⇒ ⇒ ⇒ ⇒ x+1 (x + 1) + 1 ((x + 1) + 1) + 1 ... f 2 3 ⇒ 3 g ist strikt nach a f x 3 ⇒ 3 f ist strikt nach b, aber nicht nach a f 2 x ⇒ f 2⊥ ⇒ ⊥ Prof. Dr. Margarita Esponda weil die Auswertung von x nicht terminiert Funktionale Programmierung Haskell-Programm Ein Haskell-Programm besteht aus einer Reihe von Funktionen und einem Ausdruck, der ausgewertet werden soll. Name.hs rectArea a b = a*b ✴ Eine Reihe von Funktionsdefinitionen werden in eine Skript-Datei geschrieben ✴ mit der Kommandozeile rectPerimeter a b = 2*(a+b) :load Name.hs wird diese vom HaskellInterpreter gelesen. (rectArea 4 5) + (rectArea 2 1) ✴ Prof. Dr. Margarita Esponda Dann können Ausdrücke damit ausgewertet werden. Funktionale Programmierung Berechnungsmodel • Funktionsdefinitionen werden als Ersetzungsregeln interpretiert • Ausdrücke werden damit zu einem Wert (Normalform) reduziert • Hat ein Ausdruck keine reduzierbaren Teilausdrücke, so ist er in Normalform • In Haskell werden Ausdrücke nur nach Bedarf ausgewertet. Prof. Dr. Margarita Esponda Funktionale Programmierung Pseudo-Funktionen mit Seiteneffekte Eingabe der Zeit abhängig ist e1 e2 . . en F a Der Zustand einer globalen Variable wird verändert Eine Funktion, die während Ihrer Berechnung globale Daten in irgend ein Speichermedium manipuliert, verändert damit der Zustand der AusführungUmgebung und produziert damit Seiteneffekte. Prof. Dr. Margarita Esponda Funktionale Programmierung keine Seiteneffekte ⇒ Referentielle Transparenz Vorteile der referentiellen Transparenz - Die Korrektheit der Programme oder einfache Eigenschaften können mit klassischen mathematischen Verfahren geprüft werden. - Die Wartung ist einfacher, weil die Auswertung von Teilausdrücken Kontext und Zeit unabhängig sind. - Wichtige Optimierungen durch den Compiler sind möglich - Parallele Auswertung von Teilausdrücke - gleiche Ausdrücke werden nur ein mal ausgewertet - usw. Prof. Dr. Margarita Esponda Funktionale Programmierung Datentypen ✴ Ein Datentyp entspricht dem Wertbereich, den die Argumente oder das Ergebnis einer Funktion haben können. ✴ Jeder Ausdruck in Haskell hat einen wohldefinierten Datentyp. ✴ Funktionen werden oft nur für bestimmte Datentypen definiert. ✴ Der Datentyp von Variablen oder Ausdrücken wird durch explizite Deklaration oder durch Typ-Inferenz festgestellt. Prof. Dr. Margarita Esponda Funktionale Programmierung Datentypen Explizite Deklaration des Datentyps einer Funktion Syntax: Funktionsname :: Funktionstyp Beispiele: Typ-Signatur sum :: Int -> Int -> sum Prof. Dr. Margarita Esponda a b = Int a + b doppel :: Int -> Int doppel 2*a a = pi :: double pi 3.141598 = Konstante Funktion Funktionale Programmierung Datentypen Haskell hat ein statisches Typsystem Der Datentyp der Funktionen wird statisch während der Übersetzungszeit des Programms abgeleitet. Vorteile: • Datentyp-Fehler werden früher erkannt • durch die Reduzierung der Typ-Überprüfung reduziert sich die Ausführungszeit Prof. Dr. Margarita Esponda Funktionale Programmierung Grundlegende Datentypen Int ganzzahlige Werte (-231 … 231-1) Integer ganzzahlige Werte (unbeschränkt) Bool Wahrheitswerte True Char Zeichen 'a' Float Gleitkommazahlen (32 Bits) Double Gleitkommazahlen (64 Bits) Typ1 -> Typ2 Prof. Dr. Margarita Esponda Funktionen False '1' '+' Funktionale Programmierung Konstanten (Literale) Konstante Werte, die im Programm direkt geschrieben werden, besitzen einen Typ, der sich aus der Schreibweise ergibt. Typ Beispiele: Ganze Zahlen: 345 Gleitpunktzahlen 3.45 Zeichen 'A' Zeichenketten "Zeichenkette" Prof. Dr. Margarita Esponda 0 10 Integer 0.0 'a' '1' Double '+' '\n' Char [Char] Funktionale Programmierung Arithmetische Operationen Symbol Operator Priorität + * / `div` `mod` ** ^ Addition 4 Subtraktion 4 Multiplikation 3 Division 3 Ganzzahlige Division 3 Rest 3 Ganzzahlige Potenz 2 Potenz 2 die niedrigeren Zahlen entsprechen einer höheren Priorität Arithmetische Operatoren mit der gleichen Priorität sind linksassoziativ Prof. Dr. Margarita Esponda Funktionale Programmierung Jeder Ausdruck hat einen Wert und einen Datentyp Typ Wert 1 `div` 2 ⇒ 0 Integer 1/2 ⇒ 0.5 Double 1 `mod` 2 ⇒ 1 Integer -6 `mod` 4 ⇒ -2 Integer 0.0 ^ 0 ⇒ 1.0 Double 3/0 ⇒ Infinity Double sqrt(-5) ⇒ NaN Double 0/0 ⇒ NaN Double (3/0)/(2/0) ⇒ NaN Double Prof. Dr. Margarita Esponda keine Zahl Funktionale Programmierung Kommentare Blockkommentare {- …….Blockkommentare …… …………………………….…. -} Zeilenkommentare -- Zeilenkommentare Prof. Dr. Margarita Esponda Funktionale Programmierung Vergleichsoperatoren Priorität Prof. Dr. Margarita Esponda Kleiner < 5 Größer > 5 Kleiner oder gleich <= 5 Größer oder gleich >= 5 Gleichheit == 6 Ungleichheit /= 6 Funktionale Programmierung Fallunterscheidung Allgemeine Form: if <bool-Ausdruck> then <Ausdruck> else <Ausdruck> Beispiel: sign :: Int -> Int sign x = if x > 0 then 1 else if x < 0 then -1 else 0 Prof. Dr. Margarita Esponda Funktionale Programmierung Case-Verteiler Beispiele: Übersetzung der Tonnamen von Deutsch zu Italienisch german2italian :: Char -> String german2italian x = case x of 'c' → "do" 'd' → "re" 'e' → "mi" 'f' → "fa" 'g' → "sol" 'a' → "la" 'h' → "si" Prof. Dr. Margarita Esponda Funktionale Programmierung Logische Operatoren Priorität Logische Negation not 1 UND ODER && || 7 8 Beispiele: True || False ⇒ True True && False ⇒ False not ( True || undefined ) ⇒ False not ( True && undefined ) ⇒ *** Exception: Prelude.undefined Prof. Dr. Margarita Esponda Funktionale Programmierung Logische Operatoren Die Funktion fromEnum gibt den ASCII-Code des Zeichens zurück. Beispiele: char2Digit :: Char -> Int char2Digit x = if (fromEnum x) >= (fromEnum '0') && (fromEnum x) <= (fromEnum '9') then (fromEnum x) - (fromEnum '0') else error "wrong argument value ..." Prof. Dr. Margarita Esponda Funktionale Programmierung Gibt es Funktionsnamen, die nicht erlaubt sind? Schlüsselwörter sind reserviert und können nicht als Funktionsnamen bzw. Namen für Funktionsargumente verwendet werden. Beispiel: Prof. Dr. Margarita Esponda if where then error else not case undefined of NaN let infinity usw. Funktionale Programmierung Lokale Funktionsdefinitionen f :: Float -> Float -> Float f x y = (a+1)*(b+2) where a = (x+y)/2 b = (x+y)/3 Prof. Dr. Margarita Esponda Funktionale Programmierung Guards sign :: Int -> Int sign x |x<0 | x == 0 |x>0 = -1 = 0 = 1 sign :: Int -> Int sign x |x>0 = 1 | x == 0 = 0 | otherwise = -1 otherwise :: bool otherwise = True Prof. Dr. Margarita Esponda Funktionale Programmierung Guards Allgemeine Syntax: <Funktionsname> a1 a2 … an | <guard1> = <expression1> | <guard2> = <expression2> ... | <guardm> = <expressionm> Prof. Dr. Margarita Esponda Funktionale Programmierung Struktur eines Haskell-Programms Ein Haskell-Programm besteht aus einem oder mehreren Modulen. Ein Modul besteht aus Funktionsdefinitionen und Typ-Deklarationen. Beispiel: haelfte:: Int -> Int -- Typ-Deklaration haelfte x = x `div` 2 -- Funktionsdefinition Prof. Dr. Margarita Esponda Funktionale Programmierung Programmausführung Compiler Die Programme werden direkt in die Maschinensprache des jeweiligen Rechners übersetzt. Das übersetzte Programm wird dann direkt von der Hardware interpretiert. Interpreter Drei Wege bis zur Programmausführung Die Programme werden nicht übersetzt, sondern direkt von einem Interpreter-Programm ausgeführt. Compiler + Interpreter (virtuelle Maschine) Die Programme werden in eine Zwischensprache übersetzt und von einer so genannten virtuellen Maschine (vereinfachter Interpreter) ausgeführt. Prof. Dr. Margarita Esponda Funktionale Programmierung Programmausführung Compiler Programm in Maschinensprache Programm in einer höheren Programmiersprache 0000011101101011 Übersetzer 0101010111110101 0000101010111111 1111110101010110 0010101010111110 .... Beispiele: Prof. Dr. Margarita Esponda C, C++, Das Programm kann direkt von der Hardware ausgeführt werden Funktionale Programmierung Programmausführung Interpreter Programm in einer höheren Programmiersprache Quellprogramm read (a); read (b); if (a<b) then a = a*a; else a = a+b; print a; Der Interpreter Interpreter wird direkt von der Hardware ausgeführt Das Programm wird hier interpretiert Beispiel: Skriptsprachen: JavaScript, JScript, Python, Tcl/Tk, VBA usw. Prof. Dr. Margarita Esponda Funktionale Programmierung Programmausführung Programm in einer höheren Programmiersprache read (a); read (b); if (a<b) then a = a*a; else a = a+b; print a; Compiler + Interpreter Programm in einer Zwischensprache Übersetzer LOAD LOAD MULT #1 C #2 B #1 #2 #3 LOAD ADD STORE LOAD ADD #4 #3 #1 #4 #3 A #4 #1 C A #4 #1 Der Zwischencode Beispiele: Java wird hier interpretiert Interpreter Virtuelle Maschine Der Interpreter wird direkt von der Hardware ausgeführt. Prof. Dr. Margarita Esponda Funktionale Programmierung Programmausführung in Haskell Compiler Interpreter GHCI Haskell (GHC) Haskell (WinHugs) Glasgow Haskell Compiler hello.hs main = putStrLn "Welcome to FU!" $ ghc --make -o hello hello.hs $ ./hello Welcome to FU! Prof. Dr. Margarita Esponda HUGS Haskell User's Gofer System