Universität Bielefeld Programmieren in Haskell Giegerich Programmieren in Haskell WS 2011/2012 Strictness Control More on fold Strukturelle Rekursion Robert Giegerich1 Universität Bielefeld AG Praktische Informatik 11. Dezember 2013 1 [email protected] Vorschau Universität Bielefeld Programmieren in Haskell Giegerich Strictness Control Themen heute: Funktionen höherer Ordnung (Fortsetzung) künstliche Striktheit mehr zu fold Strukturelle Rekursion für alle (rekursiven) Datentypen More on fold Strukturelle Rekursion Funktionen höherer Ordnung 1 Zum Beispiel die Funktions-Komposition: infixr 9 . 2 3 4 5 6 1 (.) :: ( b -> c ) -> ( a -> b ) -> a -> c f . g = \ x -> f ( g x ) Oder: (.) :: ( b -> c ) -> ( a -> b ) -> a -> c (.) f g x = f ( g x ) Übersichtlich für Algorithmen mit mehreren Phasen > treeSort = sortTree . build Universität Bielefeld Programmieren in Haskell Giegerich Strictness Control More on fold Strukturelle Rekursion 2 3 4 > compile = writeCode .m - opt . codegen . implmap . > transform . semCheck . parse . tokenize Das zweite Beispiel beschreibt die typischen Phasen eines Compilers $, $! und seq Universität Bielefeld Programmieren in Haskell 1 Oder sogar die Funktionsanwendung als Funktion: infixr 0 $ 2 3 4 ($) f $ x :: ( a -> b ) -> a -> b = f x -- $ heisst " apply " Giegerich Strictness Control More on fold Strukturelle Rekursion infix application operator spart Klammern 1 2 Strikte Funktionsanwendung > infixr 0 $ ! > f $ ! x = x ‘seq ‘ f x -- = f x , aber strikt seq ist die sequentielle Auswertung und dient der Vermeidung unerwünschter Laziness. $, $! und seq (2) Universität Bielefeld Programmieren in Haskell seq ist eingebaute Funktion mit den Eigenschaften 1 2 Giegerich a ‘seq‘ b = ⊥, falls a = ⊥ (1) a ‘seq‘ b = b, falls x 6= ⊥ (2) Es ändert ggf. Laufzeit (schneller oder langsamer), Speicherplatz (i.A. weniger) und Terminierungsverhalten (falls überhaupt problematisch), nicht aber die Ergebnisse einer Funktion wenn sie definiert ist. Vergleiche: let { cond a b c = if a then b else c ; x = x } in ( cond True 1) $ x 3 4 5 let { cond a b c = if a then b else c ; x = x } in ( cond True 1) $ ! x Strictness Control More on fold Strukturelle Rekursion foldr1 Universität Bielefeld Programmieren in Haskell Giegerich Strictness Control 1 2 3 4 Listerverarbeitung ohne Startwert, von rechts: foldr1 :: ( a -> a -> a ) -> [ a ] -> a foldr1 (*) = f where f [ a ] = a f ( a : b : xs ) = a * f ( b : xs ) Das letzte Listenelement dient als Startwert More on fold Strukturelle Rekursion foldl1 Universität Bielefeld Programmieren in Haskell Giegerich Strictness Control More on fold 1 2 ... und das Gleiche von links her: foldl1 :: ( a -> a -> a ) -> [ a ] -> a foldl1 (*) ( x : xs ) = foldl (*) x xs Das erste Listenelement dient als Startwert Strukturelle Rekursion foldl1-Beispiele Universität Bielefeld Programmieren in Haskell Giegerich Strictness Control 1 2 minimum = foldl1 min maximum = foldl1 max Geht’s kürzer? More on fold Strukturelle Rekursion Wiederholung: Strukturelle Rekursion Universität Bielefeld Programmieren in Haskell Giegerich Schema: Strictness Control f :: [σ] -> τ f [] = e1 f (a : as) = e2 where s = f as wobei e1 und e2 Ausdrücke vom Typ τ sind und e2 die Variablen a, as und s (nicht aber f ) enthalten darf. More on fold Strukturelle Rekursion Strukturelle Rekursion Universität Bielefeld Programmieren in Haskell Giegerich Strictness Control Viele Algorithmen auf Listen folgen dem Schema der strukturellen Rekursion fold- Funktionen unterstützen dies als Higher-Order-Functions Was ist z.B. mit insert? More on fold Strukturelle Rekursion Beipiel: insert Universität Bielefeld Programmieren in Haskell 1 2 3 4 5 6 7 8 insert hat 2 Argumente – formell keine strukturelle Rekursion Aber: insert a folgt dem Schema > insert x [] = [ x ] > insert x ( y : ys ) > | x <= y = x : y : ys > | x > y = y :( insert x ys ) Explizit: > ins [] = \ a -> [ a ] > ins (a ’: as ) = \ a -> if a <= a ’ > then a :a ’: as > else a ’:( ins as ) a ⇒ Unnatürlich? Giegerich Strictness Control More on fold Strukturelle Rekursion Erweitertes Rekursionsschema Universität Bielefeld Programmieren in Haskell Giegerich g :: σ1 → [σ2 ] → τ g i [] = e1 g i (a : as) = e2 where s = g e3 as Ausdruck Variablen e1 e2 e3 enthält i kann i, a, as und s enthalten kann i, a und as enthalten Strictness Control More on fold Strukturelle Rekursion Strukturelle Rekursion auf Bäumen Universität Bielefeld Programmieren in Haskell weitere Datenstruktur: Bäume Giegerich Modellierung in Haskell Strictness Control → als algebraischer Datentyp More on fold Example Strukturelle Rekursion · · · 1 2 · 3 4 5 Eine von vielen Baum-Varianten Universität Bielefeld Programmieren in Haskell Welche Eigenschaften sollen unsere Bäume haben? an den Blättern stehen Daten jeder innere Knoten hat 2 Kinder Darstellung: Bäume werden in der Informatik von oben nach unten gezeichnet Begriffe: Wurzel Knoten (Blatt, innerer Knoten) Kante Tiefe Giegerich Strictness Control More on fold Strukturelle Rekursion Tree Datentyp Universität Bielefeld Programmieren in Haskell Giegerich Strictness Control Tree-Datenyp 1 2 3 4 data Tree a = Leaf a | Br ( Tree a ) ( Tree a ) | Nil deriving Show More on fold Strukturelle Rekursion Beispiel Universität Bielefeld Programmieren in Haskell Giegerich Br Strictness Control Br Br Br Leaf Leaf Leaf 5 Leaf Leaf 1 2 3 1 2 Br More on fold 4 ( Br ( Leaf 1) ( Leaf 2) ) ( Br ( Br ( Leaf 3) ( Leaf 4) ( Leaf 5))) Strukturelle Rekursion Strukturelle Rekursion auf Bäumen Universität Bielefeld Programmieren in Haskell Giegerich Schema der strukturellen Rekursion: f :: Tree σ -> τ f Nil = e1 f (Leaf a) = e2 f (Br l r) = e3 where sl = f l sr = f r e3 darf dabei l, r, sl und sr enthalten, nicht aber f . Strictness Control More on fold Strukturelle Rekursion Strukturelle Rekursion auf Bäumen 1 2 3 4 data Tree a = Leaf a | Br ( Tree a ) ( Tree a ) | Nil deriving Show Rekursionsbasis (Nil) Das Problem wird für den leeren Baum gelöst. Rekursionsbasis (Leaf a) Das Problem wird für das Blatt Leaf a gelöst. Rekursionsschritt (Br l r) Um das Problem für den Baum Br l r zu lösen, werden rekursiv Lösungen für l und r bestimmt, die zu einer Lösung für Br l r erweitert werden. Universität Bielefeld Programmieren in Haskell Giegerich Strictness Control More on fold Strukturelle Rekursion Beispiel: Berechnung der Baumgröße Universität Bielefeld Programmieren in Haskell Giegerich Strictness Control More on fold 1 2 3 4 size size size size :: Tree a -> Integer Nil = 1 ( Leaf _ ) = 1 ( Br l r ) = size l + size r Strukturelle Rekursion Beispiel: Berechnung der Baumtiefe Universität Bielefeld Programmieren in Haskell Giegerich Strictness Control More on fold 1 2 3 4 depth depth depth depth :: Tree a -> Integer Strukturelle Rekursion Nil = 0 ( Leaf _ ) = 0 ( Br l r ) = max ( depth l ) ( depth r ) + 1 Beispiel: Blätter aufzählen Universität Bielefeld Programmieren in Haskell Giegerich Strictness Control 1 2 3 4 > > > > leaves leaves leaves leaves :: Tree a -> [ a ] Nil = [] ( Leaf a ) = [ a ] ( Br l r ) = leaves l ++ leaves r ⇒ Geht es nicht besser? More on fold Strukturelle Rekursion Verstärkung der Rekursion (Einbettung) Universität Bielefeld Programmieren in Haskell Exkurs zu leaves 1 2 3 4 5 6 7 Giegerich Verstärkung der Rekursion Strictness Control wie bei der rekursiven Listenfunktion reverse More on fold fleaves :: Tree a -> [ a ] fleaves t = f t [] where f :: Tree a -> [ a ] -> [ a ] f Nil y = y f ( Leaf a ) y = a : y f ( Br l r ) y = f l ( f r y ) Und warum ist das nun schneller? Strukturelle Rekursion Apropos reverse Universität Bielefeld Programmieren in Haskell Giegerich 1 2 1 2 3 Die langsame Version: > slowreverse [] = [] > slowreverse ( x : xs ) = slowreverse xs ++ [ x ] Schneller durch Einbettung > fastreverse xs = f xs [] where > f [] ys = ys > f ( x : xs ) ys = f xs ( x : ys ) Strictness Control More on fold Strukturelle Rekursion fold auf Bäumen Universität Bielefeld Programmieren 1 2 3 4 5 1 2 3 in Haskell Die Funktionen size, depth, leaves folgen alle dem Giegerich gleichen Schema. > foldTree :: b - >(a - > b ) - >(b - >b - > b ) - > Tree a -> Strictness b Control > foldTree nil leaf br = f where More on fold > f Nil = nil Strukturelle > f ( Leaf a ) = leaf a Rekursion > f ( Br l r ) = br ( f l ) ( f r ) Damit erhalten wir > size ’ = foldTree 1 (\ x - >1) (+) > depth ’ = foldTree 0 (\ x - >0) (\ x y - > max x y +1) > leaves ’= foldTree [] (\ x - >[ x ]) (++) Das findet man einfach durch Anwendung der Vogelperspektive fold im Allgemeinen Verallgemeinerung: für jeden algebraischen Datentyp T können wir eine foldT-Funktion definieren für jeden Konstruktor hat sie eine passende Funktion als Argument, sowie ein t ∈ T in der Vogelperspektive werden die Konstruktoren durch die passenden Funktionen ersetzt (und anschließend die entstandene Formel ausgerechnet). Je komplizierter der Datentyp, deso mehr gewinnt man durch die Verwendung der fold-Operation 1 PS: Und was tut die folgende Funktion f? > f = foldTree Nil Leaf Br Universität Bielefeld Programmieren in Haskell Giegerich Strictness Control More on fold Strukturelle Rekursion Das allgemeine Rekursionsschema Universität Bielefeld Programmieren in Haskell Giegerich data T a1 . . . am = C1 t11 . . . t1n1 | ... | Cr tr 1 . . . trnr Wir unterscheiden zwei Arten von Argumenten: rekursive (d.h. tij ist gleich T a1 . . . am ) und nicht-rekursive. Seien li1 ,. . . , lipi mit 1 ≤ li1 < li2 < · · · < lipi ≤ ni die Positionen, an denen der Konstruktor Ci rekursiv ist Strictness Control More on fold Strukturelle Rekursion Das allgemeine Schema der strukturellen Rekursion f :: T σ1 . . . σm -> τ f (C1 x11 . . . x1n1 ) = e1 where s11 = f x1l11 = f x1l1p1 f (Cr xr 1 . . . xrnr ) = er where sr 1 = f xrlr 1 = f xrlrpr ... ... srpr Programmieren in Haskell Giegerich Strictness Control More on fold ... s1p1 Universität Bielefeld Der Ausdruck ei darf die Variablen xi1 , . . . , xini und die Variablen si1 , . . . , sipi enthalten. Ist pi = 0, so spricht man von einer Rekursionsbasis, sonst von einem Rekursionsschritt. Strukturelle Rekursion Fazit Universität Bielefeld Programmieren in Haskell Giegerich Strictness Control Strukturelle Rekursion für jeden algebraischen Datentyp direkte Orientierung an den Konstruktoren lassen sich alle Probleme mit struktureller Rekursion lösen? strukturelle versus wohlfundierte Rekursion wie war das mit der Implementierung der Registermaschine? More on fold Strukturelle Rekursion