Funktionale Programmierung ALP I Funktionen höherer Ordnung SS 2011 Prof. Dr. Margarita Esponda Prof. Dr. Margarita Esponda Funktionale Programmierung Funktionstypen Funktionen haben einen Datentyp, der folgende allgemeine Form hat: functionname :: T1 -> T2 , wobei Beispiel: T1, T2 wiederum beliebige Datentypen sind T2 T1 Der Datentyp einer Funktion ist rechtsassoziativ. ( Integer -> Integer ) add x y = ((+) x ) y add :: Integer -> Die Funktionsapplikation dagegen ist linksassoziativ. Prof. Dr. Margarita Esponda Funktionale Programmierung Currying Eine Funktion mit mehr als einem Argument kann als eine Verschachtelung von Funktionen interpretiert werden, in der jede Funktion nur ein Argument bekommt. Haskell hat implizites Currying! Beispiel: mult :: Int → ( Int → ( Int → Int ) ) mult x y z = x * y * z mult a b c => ( mult a ) b c => ((mult a ) b) c Die mult-Funktion ist curryfiziert! Prof. Dr. Margarita Esponda Funktionale Programmierung Currying f :: t1 → t 2 → ... t n t1 t2 t3 .. . t n −1 Prof. Dr. Margarita Esponda f ( f t1 ) t1 ..t 2 . t n −1 f tn ( ( f t1 ) t 2 ) (..(( ft1 ) t 2 ).. t n −1 ) tn Funktionale Programmierung Funktionstypen Eine nicht curryfizierte Definition der mult-Funktion sieht wie folgt aus: mult :: (Integer, 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: mult (3, 4, 2) Prof. Dr. Margarita Esponda ⇒ 24 Funktionale Programmierung Sektionen Jede arithmetische Infix-Operation kann in eine Curried-Funktion verwandelt werden, indem der Operator in runden Klammern und als Präfix-Funktion geschrieben wird. Beispiel: (+) 3 4 dann sieht eine partielle Auswertung der Funktion wie folgt aus: (+ 3) 4 Funktion, die zu einem beliebigen Argument die Zahl 3 addiert Im allgemeinen, wenn ⊕ ein Infix-Operator ist, werden Ausdrücke mit der Form (⊕ x) oder (x ⊕) Sektionen genannt. Prof. Dr. Margarita Esponda Funktionale Programmierung Funktionen höherer Ordnung Eine Funktion wird als Funktion höherer Ordnung bezeichnet, wenn Funktionen als Argumente verwendet werden oder wenn eine Funktion als Ergebnis zurück gegeben wird. Beispiel: twoTimes :: ( a -> a ) -> a -> a twoTimes f x = f ( f x ) Anwendung: twoTimes quadrat 3 => quadrat (quadrat 3) => 81 Prof. Dr. Margarita Esponda Funktionale Programmierung Funktionen höherer Ordnung Typische Beispielsfunktion: map :: (a -> b) -> [a] -> [b] map f [] = [] map f (x:xs) = f x : map f xs map f [2, 3, 6, 0] => [f(2), f(3), f(6), f(0)] Die Funktion f wird auf jedes Element der Liste angewendet map (^2) [2, 3, 6, 0] Prof. Dr. Margarita Esponda => [4, 9, 36, 0] Funktionale Programmierung Funktionen höherer Ordnung Eine andere Definition der map-Funktion mit Listen-Generatoren ist: map :: (a -> b) -> [a] -> [b] map f xs = [ f x | x<-xs ] Anwendung: map length ["Eins", "Zwei", "Drei", "Vier"] => [4, 4, 4, 4] Prof. Dr. Margarita Esponda Funktionale Programmierung Funktionen höherer Ordnung Die filter-Funktion Die filter-Funktion soll aus einer Liste nur die Elemente auswählen, die eine bestimmte Bedingung erfüllen. Beispiel: filter (<3) [2,5,0,1,7] Prof. Dr. Margarita Esponda => [2,0,1] Funktionale Programmierung Funktionen höherer Ordnung Die filter-Funktion 1. Definition: filter :: (a -> Bool) -> [a] -> [a] filter p [] = [] filter p (x:xs) Bedingung |p x = x : filter p xs | otherwise = filter p xs 2. Definition: filter :: (a -> Bool) -> [a] -> [a] filter p xs = [ x | x <- xs, p x ] Prof. Dr. Margarita Esponda Funktionale Programmierung Funktionen höherer Ordnung Die filter-Funktion Beispiel: Quicksort-Algorithmus mit Hilfe der filter-Funktion qsort :: (Ord a) => (a -> a -> Bool) -> [a] -> [a] qsort p [] = [] qsort p [x] = [x] qsort p (x:xs) = qsort p (filter (p x) xs) ++ [x] ++ qsort p (filter (np x) xs) where np x y = not (p x y) Prof. Dr. Margarita Esponda Funktionale Programmierung Funktionen höherer Ordnung Nehmen wir an, wir möchten alle Zahlen innerhalb einer Liste miteinander addieren addAll:: (Num a) => [a] -> a addAll [] = 0 addAll (x:xs) = x + addAll xs oder die Und-Operation über alle Elemente einer Liste berechnen trueAll:: [Bool] -> Bool trueAll [] = True trueAll (x:xs) = x && (trueAll xs) Prof. Dr. Margarita Esponda Funktionale Programmierung Funktionen höherer Ordnung Gemeinsamkeiten von beiden Funktionen sind: - Binär-Operator - konstanter Wert, wenn die Liste leer ist. - gleiches Rekursions-Muster Wir können eine verallgemeinerte Funktion definieren, die beide Probleme löst Verallgemeinerungen sind immer gut! Beispiel: trueAll = betweenAll (&&) True addAll = betweenAll (+) 0 multAll = betweenAll (*) 1 Prof. Dr. Margarita Esponda Funktionale Programmierung Funktionen höherer Ordnung Verallgemeinerungen sind immer gut! betweenAll :: (a -> a -> a) -> a -> [a] -> a Binäre Operation Wert der Funktion, wenn die Liste leer ist betweenAll f k [] = k betweenAll f k (x:xs) = f x (betweenAll f k xs) Prof. Dr. Margarita Esponda Funktionale Programmierung Funktionen höherer Ordnung foldr-Funktion In Haskell ist bereits eine allgemeine Funktion vordefiniert, die Faltungs-Operator genannt wird Definition: foldr f z [] =z foldr f z (x:xs) = f x (foldr f z xs) foldr (*) 1 [1,2,3,4] : 1 1 : 2 3 2 * 3 : 4 Prof. Dr. Margarita Esponda => : * [] => * 4 * 1 24 Funktionale Programmierung Funktionen höherer Ordnung foldr-Funktion Haskell: foldr :: (a → b → b) → b → [a] → b foldr f z [] =z foldr f z (x:xs) = f x (foldr f z xs) Beispiel: len :: [a] → Int len xs = foldr onePlus 0 xs where onePlus :: a → Int → Int onePlus x n = 1 + n Prof. Dr. Margarita Esponda Funktionale Programmierung Funktionen höherer Ordnung Folgende Standard-Funktionen von Haskell können mit Hilfe des Faltungs-Operators definiert werden: Beispiele: Prof. Dr. Margarita Esponda sum = foldr (+) 0 product = foldr (*) 1 or = foldr (||) False and = foldr (&&) True Funktionale Programmierung Funktionen höherer Ordnung foldl-Funktion foldl :: (b → a → b) → b → [a] → b Definition: foldl f z [] =z foldl f z (x:xs) = foldl f (f z x) xs foldl f k [8,6,4,2] f : 8 : 6 f : 2 Prof. Dr. Margarita Esponda => : 4 f f [] k 4 6 8 2 Funktionale Programmierung Beispiele: Faltungs-Operatoren foldr (*) 1 [1..5] => 120 Fakultät-Funktion factorial n = foldr (*) 1 [1..n] Potenz-Funktion für positive ganzzahlige Potenzen pow b n = foldl (*) 1 (take n [b,b..b]) foldr max 0 [-6,3,6,12,14,0,23]? Prof. Dr. Margarita Esponda Funktionale Programmierung Funktionen höherer Ordnung zip-Funktion zip (x:xs) (y:ys) = (x,y) : zip xs ys zip xs = [] ys Die zip-Funktion kombiniert die Elemente aus zwei Listen und liefert eine Liste von Tupeln zurück. Beispiel: zip [1..] ["abcd"] ⇒ [(1,'a'), (2,'b'), (3,'c'), (4,'d')] Prof. Dr. Margarita Esponda Funktionale Programmierung Funktionen höherer Ordnung zipWith-Funktion Die zipWith-Funktion bekommt zwei Listen und eine Funktion als Parameter und berechnet eine neue Liste, indem jeweils die Elemente der beiden Listen mit der angegebenen Funktion verknüpft werden. zipWith :: (a -> b -> c) -> [a] -> [b] -> [c] zipWith f (x:xs) (y:ys) = (f x y):(zipWith f xs ys ) zipWith f _ _ = [] Beispiel: zipWith (^) [1, 2, 3] [0, 3, 2] Prof. Dr. Margarita Esponda ⇒ [1, 8, 9] Funktionale Programmierung Funktionen höherer Ordnung Anwendungsbeispiel der zipWith-Funktion: Skalar-Produkt von zwei Vektoren v1 . v2 v1 = (x1, x2, .. , xn) v2 = (y1, y2, .. , yn) ist v1 . v2 = x1. y1 + x2 . y2 + .. + xn. yn skalarProd ::[Int] -> [Int] -> Int skalarProd xs ys Prof. Dr. Margarita Esponda = foldl (+) 0 (zipWith (*) xs ys) Funktionale Programmierung Funktionen höherer Ordnung Funktionskomposition A g B f f °g (.) :: (b → c) → (a → b) → (a → c) (.) f g x = f (g x) (f . g) x = f (g x) Prof. Dr. Margarita Esponda C Funktionale Programmierung Funktionskomposition Beispiele: ungerade x = (not . gerade) x ungerade = not . gerade Anwendung: ungerade 4 ⇒ (not . gerade) 4 ⇒ not (gerade 4) ⇒ not True ⇒ False Prof. Dr. Margarita Esponda equiv. Funktionale Programmierung Funktionskomposition Beispiele: twoTimes f = f . f Anwendung: ungerade 4 ⇒ (not . gerade) 4 ⇒ not (gerade 4) ⇒ not True ⇒ False Prof. Dr. Margarita Esponda Funktionale Programmierung Funktionskomposition Beispiele: rep f n | n<0 = error "rep is not defined for n<0" | n>0 = f . (rep f (n-1)) | n==0 = id where id x = x Anwendung: rep not 3 True ⇒ not (rep not 2 True) ⇒ not (not (rep not 1 True)) ⇒ not (not (not (rep not 0 True))) ⇒ not (not (not (id True))) ⇒ not (not (not (True))) ⇒ False Prof. Dr. Margarita Esponda Funktionale Programmierung Funktionen höherer Ordnung all-Funktion Entscheidet, ob alle Elemente einer Liste eine gegebene Bedingung erfüllen. all :: (a → Bool) → [a] → Bool all p xs = and [p x | x ← xs] Beispiel: all even [2 ,4 ,6 ,8] all (==3) Prof. Dr. Margarita Esponda => True [3,4,3,0,3] => False Funktionale Programmierung Funktionen höherer Ordnung any-Funktion Entscheidet, ob mindestens ein Element einer Liste eine gegebene Bedingung erfüllt. any :: (a → Bool) → [a] → Bool any p xs = or [p x | x ← xs] Beispiel: any even [2 ,3 ,6 ,8] => True any (==3) [3,4 ,3 ,0 ,3] => True Prof. Dr. Margarita Esponda Funktionale Programmierung Funktionen höherer Ordnung Wir können einen allgemeineren Quicksort-Algorithmus definieren, in dem die Vergleichsoperation angegeben wird. qsort v [] = [] qsort v [x] = [x] qsort v (x:xs) = qsort v [s|s<-xs, v s x]++[x]++qsort v [b|b<-xs, not (v b x)] Anwendungsbeispiel: qsort (<) [3,2,1, 5,4] Prof. Dr. Margarita Esponda oder qsort (>=) [3,2,1, 5,4] Funktionale Programmierung Funktionen höherer Ordnung takeWhile-Funktion So lange eine Bedingung erfüllt wird, werden Elemente aus einer Liste genommen. takeWhile :: (a → Bool) → [a] → [a] takeWhile p [] = [] takeWhile p (x:xs) | p x = x : takeWhile p xs | otherwise = [] Beispiel: takeWhile (<9) Prof. Dr. Margarita Esponda [2, 5, 7, 9, 11] => [2, 5, 7] Funktionale Programmierung Funktionen höherer Ordnung dropWhile-Funktion So lange eine Bedingung erfüllt wird, werden Elemente aus einer Liste gelöscht. dropWhile :: (a → Bool) → [a] → [a] dropWhile p [] = [] dropWhile p (x:xs) | p x = dropWhile p xs | otherwise = x:xs Beispiel: dropWhile Prof. Dr. Margarita Esponda isSpace " Hello" => "Hello" Funktionale Programmierung Berechnung der Fibonacci-Zahlen rekursiv: fib 0 = 0 fib 1 = 1 fib n = fib (n-2) + fib (n-1) Die rekursive Berechnung der Fibonacci-Zahlen hat eine exponentielle Komplexität O((1,618...)n) Prof. Dr. Margarita Esponda Funktionale Programmierung Funktionen höherer Ordnung Folgende Funktion berechnet die Fibonacci-Zahlen in linearer Zeit O(n) fibs :: [Integer] fibs = 0 : 1 : zipWith (+) fibs (tail fibs) fibs tail fibs zipWith (+) 0:1:1:2:3:5:8:... 1:1:2:3:5:... 1:2:3:5: 8:... Anwendungsbeispiel: take 40 fibs Prof. Dr. Margarita Esponda