Funktionale Programmierung ALP I Funktionale Programmierung Zusammengesetzte Datentypen in Haskell WS 2012/2013 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. Beispiele: Studierende ( "Peter" , "Meyer", 439990 ) :: ( String, String, Int ) ( 2.5, 3.0 ) Prof. Dr. Margarita Esponda :: ( Double, Double ) Punkt Funktionale Programmierung Tupel-Typ Funktionsdefinition mit Tupeln: 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 Tupel-Datentyp rgbColor :: (Int, Int, Int) → [Char] rgbColor (red, green, blue) = case (red, green, blue) of (0,0,0) → "black" (255,255,255) → "white" Prof. Dr. Margarita Esponda (255,0,0) → "red" (0,255,0) → "green" (0,0,255) → "blue" otherwise → "other color" 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 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) 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 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 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 13 Funktionale Programmierung Rekursive Funktionen Typisches Beispiel: Wie viele Permutationen kann eine Reihe von Objekten maximal haben? Die Fakultätsfunktion factorial :: Int -> Int factorial n = if n==0 then 1 else n * factorial (n-1) factorial :: Int -> Int factorial n | n==0 =1 | otherwise = n*factorial (n-1) richtig aber nicht schön factorial :: Integer -> Integer factorial n | n==0 = 1 | True Prof. Dr. Margarita Esponda = n*factorial (n-1) Funktionale Programmierung Rekursive Funktionen Die Fakultätsfunktion Pattern-Matching 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 Funktionale Programmierung Rekursive Funktionen Die Fakultätsfunktion Pattern-Matching factorial :: Int -> Int factorial n = n * factorial (n-1) factorial 0 = 1 Warning: Pattern match(es) are overlapped In the definition of `factorial': factorial 0 = ... Ok, modules loaded: factorial 0 *** Exception: stack overflow Prof. Dr. Margarita Esponda Funktionale Programmierung Rekursive Funktionen noch ein Klassiker: Größter gemeinsamer Teiler von zwei natürlichen Zahlen a, b Euklid (originaler Algorithmus) ggt :: Integer → Integer → Integer ggt p q | p>q = ggt (p-q) q | p==q = p | p<q Probleme? Prof. Dr. Margarita Esponda = ggt p (q-p) Funktionale Programmierung Rekursive Funktionen ggt :: Integer → Integer → Integer ggt p q | p>q = ggt (p-q) q | p==q = p | p<q = ggt p (q-p) Probleme? ggt 20 8 ⇒ (20-8) 8 ⇒ ggt 12 8 ⇒ ggt (12-8) 8 ⇒ ggt 8 4 ⇒ ggt 4 4 ⇒4 Prof. Dr. Margarita Esponda ggt 8 0 ⇒ ggt (8-0) 0 ⇒ ggt 8 0 ⇒ ggt 8 0 .... :( Funktionale Programmierung Rekursive Funktionen 1. Lösung: 2. Lösung: ggt a b | b == 0 =a | otherwise = ggt b (a `mod` b) ggt a b | a == 0 && b == 0 = ??? Prof. Dr. Margarita Esponda | b == 0 =a | otherwise = ggt b (a `mod` b) 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 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 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 22 Funktionale Programmierung 1 Fibonacci-Zahlen 1 2 3 5 8 Prof. Dr. Margarita Esponda 23 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 24 Funktionale Programmierung Fibonacci-Zahlen 8 13 5 1 1 2 3 21 Prof. Dr. Margarita Esponda 25 Blumen Fibonacci-Zahlen 1 2 3 5 8 13 21 34 26 Prof. Dr. Margarita Esponda Funktionale Programmierung Fibonacci-Zahlen fib(0) 0 + fib(1) fib(2) 1 1 Prof. Dr. Margarita Esponda 27 Funktionale Programmierung Fibonacci-Zahlen fib(0) fib(1) 0 1 Prof. Dr. Margarita Esponda fib(2) + 1 fib(3) 2 28 Funktionale Programmierung Fibonacci-Zahlen fib(0) fib(1) fib(2) 0 1 1 Prof. Dr. Margarita Esponda fib(3) + 2 fib(4) 3 29 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 30 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 31 Funktionale Programmierung Berechnung der Fibonacci-Zahlen 1. Lösung Beispiel: fib 7 fib 7 fib 5 fib 3 fib 1 fib 0 fib 1 fib 6 fib 4 fib 2 fib 2 fib 0 = 0 fib 1 = 1 fib n = fib (n-1) + fib (n-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 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 1 fib 2 fib 0 fib 1 fib 39 einmal berechnet fib 38 2 mal berechnet 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 36 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