Fahrplan I Praktische Informatik 3: Funktionale Programmierung Vorlesung 5 vom 13.11.2012: Funktionen Höherer Ordnung I Christoph Lüth Universität Bremen Wintersemester 2012/13 Rev. 1872 I Einführung I Funktionen und Datentypen I Rekursive Datentypen I Typvariablen und Polymorphie I Funktionen höherer Ordnung I I Funktionen höherer Ordnung II I Typinferenz I Teil II: Funktionale Programmierung im Großen I Teil III: Funktionale Programmierung im richtigen Leben 1 [29] Inhalt 2 [29] Ähnliche Funktionen der letzten Vorlesung I Pfade: c a t :: Path → Path → Path c a t Mt p = p c a t ( Cons p p s ) q s = Cons p ( c a t p s q s ) I Funktionen höherer Ordnung I Funktionen als gleichberechtigte Objekte I Funktionen als Argumente I Teil I: Funktionale Programmierung im Kleinen I r e v :: Path → Path r e v Mt = Mt r e v ( Cons p p s ) = c a t ( r e v p s ) ( Cons p Mt) Gelöst durch Polymorphie Zeichenketten: c a t :: M y S t r i n g → M y S t r i n g → M y S t r i n g c a t Empty t = t c a t ( Cons c s ) t = Cons c ( c a t s t ) Spezielle Funktionen: map, filter, fold und Freunde r e v :: M y S t r i n g → M y S t r i n g r e v Empty = Empty r e v ( Cons c t ) = c a t ( r e v t ) ( Cons c Empty ) 3 [29] Ähnliche Funktionen der letzten Vorlesung 4 [29] Muster der primitiven Rekursion k a s s e :: E i n k a u f s w a g e n → I n t k a s s e LeererWagen = 0 k a s s e ( E i n k a u f a m e ) = c e n t a m+ k a s s e e i n v e n t u r :: L a g e r → I n t inventur LeeresLager = 0 i n v e n t u r ( L a g e r a m l ) = c e n t a m+ i n v e n t u r l l e n :: M y S t r i n g → I n t l e n Empty = 0 l e n ( Cons c s t r ) = 1+ l e n s t r I Anwenden einer Funktion auf jedes Element der Liste I möglicherweise Filtern bestimmter Elemente I Kombination der Ergebnisse zu einem Gesamtergebnis E x1 x3 x2 x4 Gemeinsamkeiten: I ein Fall pro Konstruktor I linearer rekursiver Aufruf I durch Polymorphie nicht gelöst (Instanz einer Definition) E 5 [29] Ein einheitlicher Rahmen I Funktionen Höherer Ordnung Beispiele: toL :: S t r i n g → S t r i n g toL [ ] = [] toL ( c : c s ) = t o L o w e r c : toL c s I 6 [29] toU :: S t r i n g → S t r i n g toU [ ] = [] toU ( c : c s ) = t o U p p e r c : toL c s Slogan “Functions are first-class citizens.” Warum nicht . . . map f [ ] = [] map f ( c : c s ) = f c : map f c s toL c s = map t o L o w e r c s toU c s = map t o U p p e r c s I Funktion f als Argument I Was hätte map für einen Typ? 7 [29] I Funktionen sind gleichberechtigt: Ausdrücke wie alle anderen I Grundprinzip der funktionalen Programmierung I Modellierung allgemeiner Berechungsmuster I Kontrollabstraktion 8 [29] Funktionen als Argumente: map Funktionen als Argumente: filter I map wendet Funktion auf alle Elemente an I Elemente filtern: filter I Signatur: I Signatur: map :: I (α→ β ) → [ α ] → [ β ] filter Definition wie oben I map f [ ] = [] map f ( x : x s ) = f x : map f x s I (α→ Bool ) → [ α ] → [ α ] Definition filter p [] = [] f i l t e r p ( x : xs ) | p x = x : f i l t e r p xs | otherwise = f i l t e r p xs Auswertung (wie vorher): toL "ABC" map toLower (’A’:’B ’:’ C’: [ ]) toLower ’A’ : map toLower (’B’:’C’: [ ]) ... ’a ’:’ b ’:’ c’ : map toLower [ ] ’a ’:’ b ’:’ c ’: [ ] = "abc" I :: I Beispiel: l e t t e r s :: S t r i n g → S t r i n g l e t t e r s = f i l t e r isAlpha Funktionsausdrücke reduzieren durch Applikation 9 [29] Beispiel filter: Primzahlen I 10 [29] Funktionen als Argumente: Funktionskomposition Sieb des Erathostenes I I I Für jede gefundene Primzahl p alle Vielfachen heraussieben Dazu: filtern mit \n→ mod n p /=0! Namenlose (anonyme) Funktion I (◦) :: (β → γ ) → (α→ β ) → α→ γ ( f ◦ g) x = f (g x) s i e v e :: [ I n t e g e r ] → [ I n t e g e r ] sieve [] = [] s i e v e ( p : ps ) = p : s i e v e ( f i l t e r ( \n → mod n p /= 0 ) p s ) I Primzahlen im Intervall [1.. n]: I prim esTo :: I n t e g e r → [ I n t e g e r ] prim esTo n = s i e v e [ 2 . . n ] I Funktionskomposition (mathematisch) I Vordefiniert I Lies: f nach g Funktionskomposition vorwärts: (>.>) :: (α→ β ) → (β → γ ) → α→ γ ( f >.> g ) x = g ( f x ) Die ersten n Primzahlen: p r i m e s :: I n t → [ I n t e g e r ] primes n = take n ( s i e v e [ 2 . . ] ) I Nicht vordefiniert! 11 [29] η-Kontraktion I 12 [29] Partielle Applikation I Vertauschen der Argumente (vordefiniert): Funktionskonstruktor rechtsassoziativ: f l i p :: (α→ β → γ ) → β → α→ γ flip f b a = f a b a → b→ c ≡ a→ (b→ c) I I Damit Funktionskomposition vorwärts: I Inbesondere: (a → b)→ c 6= a→ (b→ c) Funktionsanwendung ist linksassoziativ: (>.>) :: (α→ β ) → (β → γ ) → α→ γ (>.>) = f l i p (◦) f a b ≡ ( f a) b I I I Da fehlt doch was?! Nein: (>.>) = flip (◦) ≡ I (>.>) f g a = flip (◦) f g a I I Partielle Anwendung von Funktionen: I η-Kontraktion (η-Äquivalenz) I Bedingung: E :: α→β, x :: α, E darf x nicht enthalten λx→ E x ≡ E Syntaktischer Spezialfall Funktionsdefinition (punktfreie Notation) f x =E x ≡ f =E Inbesondere: f (a b) 6= ( f a) b Für f :: a→ b→ c, x :: a ist f x :: b→ c (closure) Beispiele: I I I map toLower :: String→ String (3 ==) :: Int→ Bool concat ◦ map ( replicate 2) :: String→ String 13 [29] Einfache Rekursion I 14 [29] Einfache Rekursion Einfache Rekursion: gegeben durch I I I eine Gleichung für die leere Liste I eine Gleichung für die nicht-leere Liste (mit einem rekursiven Aufruf) Beispiel: kasse, inventur, sum, concat, length, (+ +), . . . x1 x3 x2 f [] = A f (x:xs) = x ⊗ f xs x4 I E I I Parameter der Definition: I Startwert (für die leere Liste) A :: b I Rekursionsfunktion ⊗ :: a -> b-> b Auswertung: f [x1,..., xn] = x1 ⊗ x2 ⊗ . . . ⊗ xn ⊗ A Auswertung: sum [4,7,3] concat [A, B, C] length [4, 5, 6] Allgemeines Muster: 4+7+3+0 A+ +B + + C+ +[] 1+ 1+ 1+ 0 15 [29] I Terminiert immer (wenn Liste endlich und ⊗, A terminieren) I Entspricht einfacher Iteration (while-Schleife) 16 [29] Einfach Rekursion durch foldr I Beispiele: foldr Einfache Rekursion I sum :: [ I n t ] → I n t sum x s = f o l d r (+) 0 x s I Basisfall: leere Liste I Rekursionsfall: Kombination aus Listenkopf und Rekursionswert I I Signatur foldr I :: Summieren von Listenelementen. Flachklopfen von Listen. c o n c a t :: [ [ a ] ] → [ a ] c o n c a t x s = f o l d r ( ++ ) (α→ β → β ) → β → [ α ] → β Definition I foldr f e [] = e f o l d r f e ( x : xs ) = f x ( f o l d r f e xs ) [ ] xs Länge einer Liste l e n g t h :: [ a ] → I n t l e n g t h x s = f o l d r ( λx n → n+ 1 ) 0 x s 17 [29] Beispiele: foldr I 18 [29] Noch ein Beispiel: rev I Kasse: k a s s e :: E i n k a u f s w a g e n → I n t k a s s e = f o l d r ( λ ( a , m) r → c e n t a m+ r ) 0 I I Listen umdrehen: r e v :: [ a ] → [ a ] rev [ ] = [] r e v ( x : x s ) = r e v x s ++ [ x ] type E i n k a u f s w a g e n = [ ( A r t i k e l , Menge ) ] Inventur Mit fold: rev xs = f o l d r snoc type L a g e r = [ ( A r t i k e l , Menge ) ] [ ] xs s n o c :: a → [ a ] → [ a ] s n o c x x s = x s ++ [ x ] i n v e n t u r :: L a g e r → I n t i n v e n t u r = f o l d r ( λ ( a , m) r → c e n t a m+ r ) 0 I Unbefriedigend: doppelte Rekursion 19 [29] Einfache Rekursion durch foldl I foldr faltet von rechts: foldr ⊗ [x1 , ..., xn ] A = x1 ⊗ (x2 ⊗ (. . . (xn ⊗ A))) I Warum nicht andersherum? foldl ⊗ [x1 , ..., xn ] A = (((A ⊗ x1 ) ⊗ x2 ) . . .) ⊗ xn I Definition von foldl: 20 [29] Beispiel: rev revisited I Listenumkehr ist falten von links: rev ’ xs = f o l d l ( f l i p I f o l d l :: (α → β → α) → α → [ β ] → α foldl f a [] = a f o l d l f a ( x : xs ) = f o l d l f ( f a x ) xs (:)) [ ] xs Nur noch eine Rekursion 21 [29] foldr vs. foldl I 22 [29] foldl = foldr Definition (Monoid) f = foldr ⊗ A entspricht (⊗, A) ist ein Monoid wenn f [] = A f (x:xs) = x ⊗ f xs I I Kann nicht-strikt in xs sein, z.B. and, or (Neutrales Element links) x⊗A=x (Neutrales Element rechts) (x ⊗ y) ⊗ z = x ⊗ (y ⊗ z) f = foldl ⊗ A entspricht (Assoziativät) Theorem Wenn (⊗, A) Monoid, dann für alle A, xs f xs = g A xs g a [] = a g a (x:xs) = g (a ⊗ x) xs I A⊗x=x foldl ⊗ A xs = foldr ⊗ A xs Endrekursiv (effizient), aber strikt in xs 23 [29] I Beispiele: length, concat, sum I Gegenbeispiel: rev 24 [29] Funktionen Höherer Ordnung: Java I I Funktionen Höherer Ordnung: C Java: keine direkte Syntax für Funktionen höherer Ordnung Folgendes ist nicht möglich: I typedef s t r u c t l i s t _ t { void ∗ elem ; struct l i s t _ t ∗ next ; } ∗list ; interface Collection { Object f o l d ( Object f ( Object a , C o l l e c t i o n c ) , Object a ) } I Aber folgendes: list interface Foldable { Object f ( Object a ) ; } interface Collection { Object f o l d ( Foldable f , Object a ) ; } I Implizit vorhanden: Funktionen = Zeiger auf Funktionen Vergleiche Iterator aus Collections Framework (Java SE 6): f i l t e r ( i n t f ( void ∗x ) , l i s t l ); I Keine direkte Syntax (e.g. namenlose Funktionen) I Typsystem zu schwach (keine Polymorphie) I Benutzung: signal (C-Standard 7.14.1) #i n c l u d e <s i g n a l . h> p u b l i c i n t e r f a c e I t e r a t o r<E> boolean hasNext ( ) ; E next ( ) ; } void ( ∗ s i g n a l ( i n t sig , void ( ∗ func ) ( i n t ) ) ) ( i n t ) ; 25 [29] Funktionen Höherer Ordnung: C 26 [29] Übersicht: vordefinierte Funktionen auf Listen II Implementierung von filter: l i s t f i l t e r ( i n t f ( void ∗x ) , l i s t { i f ( l == NULL) { r e t u r n NULL ; } else { list r; r= f i l t e r ( f , l → n e x t ) ; i f ( f ( l → elem ) ) { l → n e x t= r ; return l ; } else { free ( l ); return r ; } } } l) map :: (α→ β ) → [ α ] → [ β ] −− Auf alle anwenden f i l t e r :: (α→ Bool ) → [ α ] → [ α ] −− Elemente filtern foldr :: (α→ β → β ) → β → [ α ] → β −− Falten v. rechts foldl :: (β → α→ β ) → β → [ α ] → β −− Falten v. links t a k e W h i l e :: (α→ Bool ) → [ α ] → [ α ] d r o p W h i l e :: (α→ Bool ) → [ α ] → [ α ] −− takeWhile ist längster Prefix so dass p gilt, dropWhile der Rest any :: (α → Bool ) → [ α ] → Bool −− p gilt mind. einmal all :: (α → Bool ) → [ α ] → Bool −− p gilt für alle elem :: ( Eq α) ⇒ α → [ α ] → Bool −− Ist enthalten? z i p W i t h :: (α → β → γ ) → [ α ] → [ β ] → [ γ ] −− verallgemeinertes zip 27 [29] Zusammenfassung I I Funktionen höherer Ordnung I Funktionen als gleichberechtigte Objekte und Argumente I Partielle Applikation, η-Kontraktion, namenlose Funktionen I Spezielle Funktionen höherer Ordnung: map, filter, fold und Freunde Formen der Rekursion: I Einfache Rekursion entspricht foldr 29 [29] 28 [29]