Funktionale Programmierung ALP I Funktionale Programmierung Zusammengesetzte Datentypen in Haskell SS 2011 Prof. Dr. Margarita Esponda Prof. Dr. Margarita Esponda Funktionale Programmierung Zusammengesetzte Datentypen Tupel List String Prof. Dr. Margarita Esponda Funktionale Programmierung Zusammengesetzte Datentypen Tupel-Datentyp Ein Tupel ist eine Ansammlung von zwei oder mehreren Daten, die unterschiedliche Datentypen besitzen können. Mit Hilfe von Tupeln können zusammengehörige Daten als Einheit behandelt werden. Studierende Beispiele: ( "Peter" , "Meyer", 439990 ) :: ( String, String, Int ) ( 2.5, 3.0 ) :: ( Double, Double ) Punkt Prof. Dr. Margarita Esponda Funktionale Programmierung Tupel-Typ Funktionsdefinition mit Tupeln: distance :: (Double, Double) -> (Double, Double) -> Double distance p1 p2 = sqrt(xd^2 + yd^2) where xd = (fst p1) - (fst p2) yd = (snd p1) - (snd p2) distance :: (Double, Double) -> (Double, Double) -> Double distance (x1,y1) (x2,y2) = sqrt (squareX + squareY ) where squareX = (x1-x2) * (x1-x2) squareY = (y1-y2) * (y1-y2) Prof. Dr. Margarita Esponda Funktionale Programmierung Pattern-Matching myNot :: Bool -> Bool myNot True = False myNot False = True oder :: Bool -> Bool -> Bool oder oder False False = False x y = True oder0 :: Bool -> Bool -> Bool oder0 False False = False oder0 False True = True oder0 True False = True oder0 True True = True und :: Bool -> Bool -> Bool und True True = True und _ _ = False beliebige Argumente vom Typ Bool Prof. Dr. Margarita Esponda Funktionale Programmierung Pattern-Matching In Haskell ist es möglich durch Pattern-Matching eine Auswahl verschiedener Funktionsgleichungen zu definieren. Allgemeine Form: f p11 p12 … p1n = e1 f p21 p22 … p2n = e2 ….. f pk1 pk2 … pkn = ek Die pij Muster werden von oben nach unten und von links nach rechts geprüft. Prof. Dr. Margarita Esponda Funktionale Programmierung Tupel-Typ Ein Tupel-Typ hat folgende allgemeine Form: ( t1 , t2 , … , tn ), wobei t1 , t2 , … , tn beliebige Datentypen sind Zwei einfache Funktionen für Tupel sind: fst (x, y) = x snd (x, y) = y Die erwartete Tupel-Struktur wird in den Argumenten sichtbar Prof. Dr. Margarita Esponda Funktionale Programmierung Typ-Synonyme Typ-Synonyme werden verwendet, um die Lesbarkeit von Programmen mit Hilfe von aussagekräftigen Typ-Namen zu verbessern. Allgemeine Form: type Typname = ……. Beispiele: type Student = (String, String, Int) type Point = (Double, Double) Prof. Dr. Margarita Esponda Funktionale Programmierung Typ-Synonyme Funktionsdefinition mit Typ-Synomyme: type Point = (Double, Double) distance :: Point -> Point -> Double distance (x1,y1) (x2,y2) = sqrt (sumSq (x1-x2) (y1-y2)) where sumSq x y = x*x + y*y Prof. Dr. Margarita Esponda Funktionale Programmierung Typ-Synonyme type Complex = (Double, Double) realPart :: Complex -> Double realPart ( real, img ) = real imgPart :: Complex -> Double Text imgPart ( real, img ) = img sumC :: Complex -> Complex -> Complex sumC (r1,i1) (r2,i2) = (r1+r2, i1+i2) absC :: Complex -> Double absC ( real, img ) = sqrt( real*real + img*img ) Prof. Dr. Margarita Esponda Funktionale Programmierung Fibonacci-Zahlen Problem: Zu Beginn eines Jahres gibt es genau ein Paar neugeborener Kaninchen. Ein Paar neugeborener Kaninchen ist erst nach einem Monat fortpflanzungsfähig, sodass erst nach zwei Monaten ein neues Kaninchenpaar zur Welt gebracht werden kann. Jedes nicht neugeborene Kaninchen bekommt ein neues Kaninchenpaar monatlich. Wie viele Kaninchen gibt es nach einem Jahr, wenn keines der Kaninchen stirbt? Prof. Dr. Margarita Esponda 11 Funktionale Programmierung 1 Fibonacci-Zahlen 1 2 3 5 8 Prof. Dr. Margarita Esponda 12 Funktionale Programmierung Fibonacci-Zahlen Der Stammbaum einer Drohne 13 8 Ururgroßeltern 5 Urgroßeltern 3 Großeltern 2 Mutter 1 Drohne Prof. Dr. Margarita Esponda 13 Funktionale Programmierung Fibonacci-Zahlen 8 13 5 1 1 2 3 21 Prof. Dr. Margarita Esponda 14 Blumen Fibonacci-Zahlen 1 2 3 5 8 13 21 34 15 Prof. Dr. Margarita Esponda Funktionale Programmierung Fibonacci-Zahlen fib(0) 0 + fib(1) fib(2) 1 1 Prof. Dr. Margarita Esponda 16 Funktionale Programmierung Fibonacci-Zahlen fib(0) fib(1) 0 1 Prof. Dr. Margarita Esponda fib(2) + 1 fib(3) 2 17 Funktionale Programmierung Fibonacci-Zahlen fib(0) fib(1) fib(2) 0 1 1 Prof. Dr. Margarita Esponda fib(3) + 2 fib(4) 3 18 Funktionale Programmierung Fibonacci-Zahlen fib(0) fib(1) fib(2) 0 1 1 Prof. Dr. Margarita Esponda fib(3) 2 + fib(4) 3 fib(5) 5 19 Funktionale Programmierung Fibonacci-Zahlen fib(0) fib(1) fib(2) fib(3) 0 1 1 2 ..... fib(4) fib(5) 3 fib(n-2) + fib(n-1) 5 fib(6) fib(7) + 8 + 13 ..... fib(n) Formale rekursive Definition: fib ( 0 ) = 0 fib ( 1 ) = 1 fib ( n ) = fib ( n-2 ) + fib ( n-1 ) Prof. Dr. Margarita Esponda für alle n>1 20 Funktionale Programmierung Berechnung der Fibonacci-Zahlen 1. Lösung Beispiel: fib 7 fib 1 = 1 fib n = fib (n-1) + fib (n-2) fib 7 fib 5 fib 3 fib 1 fib 0 fib 1 fib 6 fib 4 fib 2 fib 2 fib 0 = 0 fib 4 fib 3 fib 2 fib 5 fib 3 fib 3 fib 4 fib 0 fib 1 fib 1 fib 2 fib 0 fib 1 fib 1 fib 2 fib 1 fib 2 fib 2 fib 3 fib 0 fib 1 fib 0 fib 1 fib 0 fib 1 wiederholte Berechnungen der fib-Funktion Prof. Dr. Margarita Esponda fib 1 fib 2 fib 0 fib 1 Berechnung der Fibonacci-Zahlen Beispiel: fib 7 fib 7 fib 5 fib 3 fib 1 fib 0 fib 4 fib 2 fib 1 fib 6 fib 2 fib 4 fib 3 fib 2 fib 5 fib 3 fib 3 fib 4 fib 0 fib 1 fib 1 fib 2 fib 0 fib 1 fib 1 fib 2 fib 1 fib 2 fib 2 fib 3 fib 0 fib 1 Fallgrube Wenn wir fib 40 mit unserer rekursiven Implementierung berechnen, wird: fib 0 fib 1 fib 0 fib 1 fib 39 einmal berechnet fib 38 2 mal berechnet fib 1 fib 2 fib 0 fib 1 fib 37 3 mal berechnet fib 36 5 mal berechnet fib 35 8 mal berechnet ... fib 0 165 580 141 mal berechnet Beim Aufruf von fib 40 werden 331 160 281 Funktionsaufrufe gemacht Funktionale Programmierung Berechnung der Fibonacci-Zahlen Wie viele Reduktionsschritte brauchen wir, um fib n zu berechnen? fib 0 1 fib 1 + 1 1 = + fib 2 2 2 2 fib 3 fib 4 5 3 = + fib 5 fib 6 8 13 Reduktionen 3 3 3 = + 5 5 3 = + 8 5 = 13 Die Anzahl der Reduktionen für fib n ist gleich fib (n+1) Die Anzahl der rekursive Aufrufe ist eine exponentielle Funktion von n. Prof. Dr. Margarita Esponda Funktionale Programmierung Berechnung der Fibonacci-Zahlen 2. Lösung quickFib funktioniert nur, wenn diese mit den ersten zwei Fibonacci-Zahlen gestartet wird. fib' n = quickFib 0 1 n Zähler where quickFib a b 0 = a quickFib a b n = quickFib b (a+b) (n-1) Innerhalb jedes rekursiven Aufrufs wird eine neue Fibonacci-Zahl berechnet und der Zähler verkleinert. Die neue Zahl und ihr Vorgänger werden beim nächsten rekursiven Aufruf als Parameter weitergegeben. Anzahl der Reduktionen Für die Berechnung von quickFib n benötigen wir n Reduktionen, Prof. Dr. Margarita Esponda Funktionale Programmierung Fibonacci-Zahlen 3. Lösung mit Tupeln: nextFib :: (Integer, Integer) -> (Integer, Integer) nextFib (a,b) = (b, a+b) fib n = fst ( fibTuple n ) Text fibTuple n | n==0 = (0, 1) | otherwise = nextFib (fibTuple (n-1)) Prof. Dr. Margarita Esponda 25 Funktionale Programmierung Listen Listen sind die wichtigsten Datenstrukturen in funktionalen Programmiersprachen Listen stellen Sammlungen von Objekten dar, die den gleichen Datentyp besitzen Listen sind dynamische Datenstrukturen, die mit Hilfe folgender Daten-Konstruktoren erzeugt werden können Symbol Prof. Dr. Margarita Esponda Name Bedeutung [] nil leere Liste (:) cons am Anfang anfügen Funktionale Programmierung Listen Listen sind rekursive Strukturen: Eine Liste ist entweder leer [] oder ein konstruierter Wert, der aus einem Listenkopf x und einer Restliste xs besteht. head tail x : xs Der Typ einer Liste, die Elemente des Typs t enthält, wird mit [t] bezeichnet. Prof. Dr. Margarita Esponda Funktionale Programmierung Ausdrücke mit Listen Beispiele: Ausdruck Datentyp [1, 2, 3] :: [Integer] 1: [0,3,7] :: [Integer] [ [0.3, 0.0], [] ] :: [[Double]] 'a' : "Hello" :: [Char] [( 3, 0 ), ( 2, 1)] :: [ (Integer, Integer) ] [ True, True, False ] :: [ Bool ] Allgemeine Syntax: [ e1, e2, e3, … ,en ] Prof. Dr. Margarita Esponda Syntaktische Abkürzung: e1:[e2:[e3: … :en:[]]] e1:e2: … :en:[] Funktionale Programmierung Ausdrücke mit Listen Beispiele: equiv. [1, 2, 3] 1:[2, 3] 1 : 2 : 3 : [] "hello" ['h', 'e', 'l', 'l', 'o'] 'h' : "ello" Prof. Dr. Margarita Esponda [( 3, 0 ), ( 2, 1)] ( 3, 0 ) : [( 2, 1)] [[3, 0 ], [2, 1]] [3, 0] : [2, 1] : [] Funktionale Programmierung Funktionen mit Listen kopf :: [ Integer ] -> Integer kopf (x:xs) = x kopf [] = error "ERROR: empty list" rumpf :: [ Integer ] -> [ Integer ] rumpf (x:xs) = xs rumpf [] = error "ERROR: empty list" Prof. Dr. Margarita Esponda Funktionale Programmierung Funktionen mit Listen Addiert alle Zahlen innerhalb einer Liste summe :: [Integer] -> Integer summe ls = if ls == [] then 0 else ( kopf ls ) + summe (rumpf ls) Lösung mit Pattern-Matching über der Listenstruktur summe :: [Integer] -> Integer summe [] summe (x : xs) = Prof. Dr. Margarita Esponda = 0 x + summe xs Funktionale Programmierung Funktionsdefinitionen mit Listen multList :: [Integer] -> Integer multList [] = error "the function is not defined for []" multList [x] = x multList (x:xs) = x * multList xs laenge :: [Int] -> Int laenge [] = 0 laenge (x:xs) = 1 + laenge xs Prof. Dr. Margarita Esponda Funktionale Programmierung Zehnersystem → Binärsystem 42 10 = div 2 mod 2 101010 2 42 0 21 1 10 0 5 1 2 1 0 0 1 dec2bin :: Int -> [Int] dec2bin n | n<2 = [n] | otherwise = dec2bin (n`div`2) ++ [n `mod`2] Prof. Dr. Margarita Esponda Funktionale Programmierung Funktionen mit Listen Verkettungsoperator drehe [] = [] drehe (x:xs) = drehe xs ++ [x] drehe [1,2,3] ⇒ drehe [2,3] ++ [1] ⇒ drehe [3] ++ [2] ++ [1] ⇒ drehe [] ++ [3] ++ [2] ++ [1] ⇒ [] ++ [3] ++ [2] ++ [1] Prof. Dr. Margarita Esponda Funktionale Programmierung Funktionen mit Listen 1011012 = 1*25 + 0*24 + 1*23 + 1*22 + 0*21 + 1*20 = 2(2(2(2(2(1)+0)+1)+1)+0)+1 binary2decimal xs = bin2dec ( drehe xs) bin2dec :: [Int] -> Int bin2dec [] = 0 bin2dec (x:xs) = x + 2*(bin2dec xs) Prof. Dr. Margarita Esponda Funktionale Programmierung Funktionen mit Listen bitXOr :: [Int] -> [Int] -> [Int] bitXOr [] [] = [] bitXOr (x:xs) (y:ys) = (exoder x y): (bitXOr xs ys) bitXOr _ _ = error "the two lists are not of the same length" exoder :: Int -> Int -> Int exoder x y | x==y =0 | otherwise = 1 Prof. Dr. Margarita Esponda Funktionale Programmierung Funktionstypen Funktionen haben in Haskell einen Datentyp, der folgende allgemeine Form hat: func :: T1 -> T2 , wobei T1, T2 wiederum beliebige Datentypen sind Beispiel: func :: (Char, Bool, [Int]) -> Bool -> Int add :: Integer -> Integer -> Integer add x y = Prof. Dr. Margarita Esponda x+y Funktionale Programmierung Funktionstypen Die Funktionsapplikation ist linksassoziativ Der Wert dieses Ausdrucks ist wiederum eine Funktion, die die Zahl 3 zum eingegebenen Argument addiert. add 3 7 wird als (add 3) 7 interpretiert Der Datentyp der Funktion ist dagegen rechtsassoziativ Funktionstyp nach der partiellen Auswertung der Funktion, add :: Integer -> (Integer -> Integer) Prof. Dr. Margarita Esponda Funktionale Programmierung Currying In Haskell kann eine Funktion, die zwei oder mehr Argumente erwartet, als eine Verschachtelung von Funktionen interpretiert werden, die jeweils nur ein Argument bekommen ( implizites Currying). Beispiel: mult :: Int -> Int -> Int mult x y = x * y Die mult-Funktion ist curryfiziert! Diese Definition ermöglicht eine partielle Anwendung wie z.B. sum 1, die als Ergebnis eine Funktion zurückgibt, die ein zweites Argument erwartet. inc :: Int -> Int inc = (sum 1) Prof. Dr. Margarita Esponda Funktionale Programmierung Funktionstypen Eine nicht curryfizierte Definition der add-Funktion sieht wie folgt aus: add2 :: (Integer , Integer) -> Integer Eine partielle Auswertung mit nur einem Argument ist nicht möglich, weil beide Argumente, als Tupel verpackt, eingegeben werden müssen. Anwendungsbeispiel: add2 ( 3, 4 ) Prof. Dr. Margarita Esponda Funktionale Programmierung Polymorphe Datentypen Die Angabe eines genauen Typs für ein Argument oder einen Ausdruck ist oft nicht möglich oder wünschenswert. typisches Beispiel: length [] =0 length x:xs = 1 + length xs Dann verwendet man einen allgemeineren polymorphen Typ length :: [a] -> Integer Eine Funktion, die variable Datentypen beinhaltet, wird als polymorphe Funktion bezeichnet. Prof. Dr. Margarita Esponda Funktionale Programmierung Polymorphe Funktionen Beispiele: head :: [a] -> a head (x:xs) = x tail:: [a] -> [a] tail (x:xs) = xs take :: Int -> [a] -> [a] drop:: Int -> [a] -> [a] Prof. Dr. Margarita Esponda Funktionale Programmierung Einige vordefinierte Funktionen auf Listen Name Typ Beispiel head [a] -> a head [1,2,3] => 1 tail [a] -> [a] tail [1,2,3] => [2,3] length [a] -> Int length [1,2,3] => 3 (++) [a] -> [a] -> [a] [1,2,3] ++ [1,2,3] => [1,2,3,1,2,3] (!!) [a] -> Int -> a [1,2,3,4] !! 2 => 3 take Int -> [a] -> [a] take 2 [1,2,3,4] => [1,2] drop Int -> [a] -> [a] drop 2 [1,2,3,4] => [3,4] Prof. Dr. Margarita Esponda Funktionale Programmierung Strings Strings sind Listen von Zeichen Typ-Synonym type String = [Char] Syntaktische Abkürzung [ 'H' , 'e', 'l', 'l', 'o' ] => "Hello" Haskell ruft implizit am Ende der Auswertung eines Ausdrucks die ShowFunktion an, die aus einem beliebigen Datentyp ein String erzeugt. … Prof. Dr. Margarita Esponda Funktionale Programmierung Strings Strings sind Listen von Zeichen Typ-Synonym type String = [Char] Syntaktische Abkürzung [ 'H' , 'e', 'l', 'l', 'o' ] => "Hello" Haskell ruft implizit am Ende der Auswertung eines Ausdrucks die ShowFunktion an, die aus einem beliebigen Datentyp ein String erzeugt. … Prof. Dr. Margarita Esponda