Funktionale Programmierung ALP I Einführung in Haskell SS 2011 Prof. Dr. Margarita Esponda Prof. Dr. Margarita Esponda 1 Funktionale Programmierung Was ist Haskell? Eine rein funktionale Programmiersprache - Eine Funktion liefert immer bei gleicher Eingabe das gleiche Ergebnis. - keine Seiteneffekte! - Referenzielle Transparenz. Der Wert eines Ausdrucks ist von der Zeit seine Ausführung unabhängig. Prof. Dr. Margarita Esponda 2 Funktionale Programmierung Funktionen Eingabewerte e1 e2 . . en F a nur ein Ausgabewert Das Ergebnis einer Funktion hängt nur von den Eingabewerten ab. Beispiel: Arithmetische Operationen können auch als Funktionen betrachtet werden 1 2 + 3 Prof. Dr. Margarita Esponda 3 Funktionale Programmierung Funktionen in Haskell Funktionsdefinition in Haskell: f x = x*x Beispiel: 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 4 Funktionale Programmierung Funktionsapplikation Die Anwendung einer Funktion mit konkreten Argumenten wird als Funktionsapplikation bezeichnet. Funktionsdefinition: f x y z = x*y + x*z Funktionsapplikation: f 1 0 2 ⇒ 1*0 + 1*2 ⇒0+2 ⇒2 Funktionsreduktion Die Variablen auf der rechten Seite der Funktionsdefinition werden durch die entsprechenden konkreten Argumente ersetzt. Prof. Dr. Margarita Esponda 5 Funktionale Programmierung Normalform Kann ein Ausdruck nicht mehr weiter reduziert werden, dann befindet er sich in seiner Normalform und die Berechnung ist beendet. Beispiele: Zeichenkette 3 1.0 Zahlen "text" f Funktion ohne Argumente Prof. Dr. Margarita Esponda 6 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 7 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 8 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 9 Funktionale Programmierung Auswertungsstrategien Funktionsbeispiele: z = 2*5 f x = x+x Auszuwertender Ausdruck: f z 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 z ⇒ f 10 ⇒ 10 + 10 ⇒ 20 Es wird garantiert, dass die Argumente zuerst ausgewertet werden und dann an die Funktion weitergegeben werden. f z ⇒ ⇒ ⇒ ⇒ ⇒ f (2*5) (2*5) + (2*5) 10 + (2*5) 10 + 10 20 Ohne die Argumente auszuwerten, werden zuerst die äusseren Funktionen angewendet. Prof. Dr. Margarita Esponda 10 Funktionale Programmierung 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: f z ⇒ z * z where z = 2*5 ⇒ z * z where z = 10 ⇒ 10 * 10 call-by-need ist eine Art ⇒ 100 call-by-name-Auswertungsstrategie mit Gedächtnis Prof. Dr. Margarita Esponda 11 Funktionale Programmierung 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 12 Funktionale Programmierung 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⊥ ⇒ ⊥ weil die Auswertung von x nicht terminiert Prof. Dr. Margarita Esponda 13 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) (rectArea 4 5) + (rectArea 2 1) :load Name.hs wird diese vom HaskellInterpreter gelesen. ✴ Dann können Ausdrücke damit ausgewertet werden. Prof. Dr. Margarita Esponda 14 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 15 Funktionale Programmierung Datentypen ✴ Ein Datentyp entspricht dem Wertebereich, 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 Typinferenz festgestellt. Prof. Dr. Margarita Esponda 16 Funktionale Programmierung Datentypen Explizite Deklaration des Datentyps einer Funktion Syntax: Funktionsname :: Funktionstyp Beispiele: Typ-Signatur sum :: Int -> Int -> sum a b = Int a + b doppel :: Int -> Int doppel 2*a a = pi :: double pi 3.141598 = Konstante Funktion Prof. Dr. Margarita Esponda 17 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 18 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 False '1' '+' Funktionen Prof. Dr. Margarita Esponda 19 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 0 10 Gleitpunktzahlen 3.45 Zeichen 'A' Zeichenketten "Zeichenkette" Integer 0.0 'a' '1' Double '+' '\n' Char [Char] Prof. Dr. Margarita Esponda 20 Funktionale Programmierung Arithmetische Operationen Symbol Operator Priorität + Addition 4 - Subtraktion 4 * Multiplikation 3 / Division 3 `div` Ganzzahlige Division 3 `mod` Rest 3 ^ Potenz 2 die niedrigeren Zahlen entsprechen einer höheren Priorität Arithmetische Operatoren mit der gleichen Priorität sind linksassoziativ Prof. Dr. Margarita Esponda 21 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 0/0 ⇒ NaN Double sqrt(-5) ⇒ NaN Double 3/0 ⇒ Infinity Double (3/0)/(2/0) ⇒ NaN Double keine Zahl Prof. Dr. Margarita Esponda 22 Funktionale Programmierung Kommentare Blockkommentare {- …….Blockkommentare …… …………………………….…. -} Zeilenkommentare -- Zeilenkommentare Prof. Dr. Margarita Esponda 23 Funktionale Programmierung Vergleichsoperatoren Priorität Kleiner < 5 Größer > 5 Kleiner oder gleich <= 5 Größer oder gleich >= 5 Gleichheit == 6 Ungleichheit /= 6 Prof. Dr. Margarita Esponda 24 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 25 Funktionale Programmierung Case-Verteiler Beispiele: char2Int :: Char -> Integer char2Int x = case x of '1' -> 1 '2' -> 2 '3' -> 3 '4' -> 4 '5' -> 5 digit :: String -> Integer digit text = case text of "eins" -> 1 "zwei" -> 2 "drei" -> 3 "vier" -> 4 "fünf" -> 5 Prof. Dr. Margarita Esponda 26 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 27 Funktionale Programmierung Logische Operatoren Beispiele: Die Funktion fromEnum gibt den ASCII-Code des Zeichens zurück. 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 28 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 Prof. Dr. Margarita Esponda 29 Funktionale Programmierung Guards Allgemeine Syntax: <Funktionsname> a1 a2 … an | <guard1> = <expression1> | <guard2> = <expression2> ... | <guardm> = <expressionm> otherwise :: bool otherwise = True Prof. Dr. Margarita Esponda 30 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 31 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 32 Funktionale Programmierung Rekursion Rekursion ist ein fundamentales Konzept in der Mathematik - eine Denkform für Problembeschreibung - enge Beziehung zur mathematischen Induktion Rekursion ist ein fundamentales Konzept in der Informatik - eine Methode zur Problemlösung Rekursive Funktionsdefinitionen sind die wichtigste Programmiertechnik in funktionalen Programmiersprachen. Prof. Dr. Margarita Esponda 33 33 Funktionale Programmierung Rekursive Funktionen Typisches Beispiel: Die Fakultätsfunktion Wie viele Permutationen kann eine Reihe von Objekten maximal haben? factorial :: Int -> Int factorial 0 = 1 factorial n = n * factorial (n-1) bigFactorial :: Integer -> Integer bigFactorial 0 = 1 bigFactorial n = n * bigFactorial (n-1) Prof. Dr. Margarita Esponda 34 Funktionale Programmierung Rekursive Funktionen noch ein Klassiker: Größter gemeinsamer Teiler von zwei natürlichen Zahlen a, b Euklid (originaler Algorithmus) ggt p q | p>q = ggt (p-q) q | p==q = p | p<q Probleme? Moderne Lösung: = ggt p (q-p) ggt a b | b == 0 =a | otherwise = ggt b (a `mod` b) Prof. Dr. Margarita Esponda 35 Funktionale Programmierung Pizzaproblem (Jakob Steiner, 1826) Wie viele Pizza-Teile entstehen maximal, wenn eine Pizza mit n geraden Schnitten aufgeteilt wird? maxSurfaces :: Int -> Int maxSurfaces 0 = 1 maxSurfaces n = maxSurfaces (n - 1) + n Prof. Dr. Margarita Esponda 36 Funktionale Programmierung Rekursive Funktionen Berechnung des goldenen Schnitts Zwei positive reelle Zahlen stehen im Verhältnis der goldenen Zahl, wenn a a+b = b a gilt, Goldener Schnitt = mit a > b > 0. Φ = 1+ 1 Φ goldenRatio :: Int -> Double goldenRatio n | n == 0 = 1 | n > 0 = 1 + 1 / goldenRatio (n-1) | otherwise = error "not defined for negative numbers" Prof. Dr. Margarita Esponda 37 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 38 Funktionale Programmierung Programmausführung Compiler Programm in Maschinensprache Programm in einer höheren Programmiersprache 0000011101101011 Übersetzer 0101010111110101 0000101010111111 1111110101010110 0010101010111110 .... Beispiele: C, C++, Das Programm kann direkt von der Hardware ausgeführt werden Prof. Dr. Margarita Esponda 39 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 40 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 41 Funktionale Programmierung Programmausführung in Haskell Compiler Interpreter GHCI Haskell (GHC) Haskell (WinHugs) Glasgow Haskell Compiler hello.hs main = putStrLn "Welcome to FU!" HUGS Haskell User's Gofer System $ ghc --make -o hello hello.hs $ ./hello Welcome to FU! Prof. Dr. Margarita Esponda 42