Was bisher geschah I Pattern matching I algebraische Datentypen I rekursive Datentypen (Peano-Zahlen, Listen, Binärbäume) I rekursive Funktionen I strukturelle Induktion I Rekursionsschemata I Funktionen höherer Ordnung 70 Auswertungsreihenfolge inc :: Int -> Int inc n = n + 1 2 Möglichkeiten, den Wert von inc(3 * 5) zu berechnen Es wird bei beiden Möglichkeiten derselbe Wert berechnet. (Haskell ist nebenwirkungsfrei.) sq :: Int -> Int sq x = x * x sq (3 + 1) 71 Redex Reduktion Termersetzung durch Funktionsanwendung Redex reduzierbarer Teilterm Redexe von mult ( 1 + 2 ) ( 2 + 3 ) mult :: Int -> Int -> Int mult = \x y -> x * y mult ( 1 + 2 )( 2 + 3 ) fst :: (a, b) -> a fst ( x, _ ) = x fst ( 1 + 2, 3 + 5 ) 72 Auswertungs-Strategien innermost Reduktion von Redexen, die keinen Redex enthalten (Parameterübergabe by value) outermost Reduktion von Redexen, die in keinem Redex enthalten sind (Parameterübergabe by name) (jeweils so weit links wie möglich zuerst) mult :: Int -> Int -> Int mult x = \y -> x * y mult ( 1 + 2 )( 2 + 3 ) Teilterme in λ-Ausdrücken werden nicht reduziert. (\ x -> 1 + 2) 1 73 Termination inf :: Int inf = 1 + inf Auswertung von fst (3, inf) terminiert unter outermost-Strategie, aber nicht unter innermost-Strategie Fakt Für jeden Ausdruck, für den die Auswertung unter irgendeiner Strategie terminiert, terminert auch die Auswertung unter outermost-Strategie. 74 Sharing von Teilausdrücken jeder Funktionsaufruf ist lazy: I kehrt sofort zurück I Resultat ist thunk I thunk wird erst bei Bedarf ausgewertet I Bedarf entsteht durch Pattern Matching sq :: Int -> Int sq x = x * x sq (3 + 1) data N = Z | S N nichtnull :: N -> Bool nichtnull n = case n of Z -> False ; S _ -> True x = S undefined nichtnull x Lazy Evaluation (Bedarfsauswertung) = Outermost-Reduktionsstrategie mit Sharing 75 Lazyness jeder Wert wird erst bei Bedarf ausgewertet. Listen sind Streams, der Tail wird erst bei Bedarf ausgewertet. Wann die Auswertung stattfindet, lässt sich nicht beobachten. Die Auswertung hat keine Nebenwirkungen. unendliche Datenstruktur: naturals :: [ Integer ] naturals = from 0 where from x = x : from ( x + 1 ) Liste aller Quadratzahlen? Primzahlen? 76 Unendliche Datenstrukturen inf :: Int inf = 1 + inf fst(0, inf) einsen :: [Int] einsen = 1 : einsen head einsen take 3 einsen nats :: [Int] nats = 0 : map (+1) nats takeWhile (<= 5) nats 77 Motivation: Datenströme Folge von Daten: I I I erzeugen (producer) transformieren verarbeiten (consumer) aus softwaretechnischen Gründen: diese drei Aspekte im Programmtext trennen, aus Effizienzgründen: in der Ausführung verschränken (bedarfsgesteuerte Transformation/Erzeugung) 78 Rekursive Stream-Definitionen nats = 0 : map (+1) nats fibonacci = 0 : 1 : zipWith (+) fibonacci ( tail fibonacci ) take 10 fibonacci take 1 $ dropWhile (< 200) fibonacci Welchen Wert hat bin ? bin = False : True : concat ( map ( \ x -> [ x, not x ] ) ( tail bin ) ) 79 Primzahlen Sieb des Eratosthenes alle_ab :: Int -> [ Int ] alle_ab n = n : alle_ab ( n+1 ) primzahlen :: [ Int ] primzahlen = sieb $ alle_ab 2 sieb :: [ Int ] -> [ Int ] sieb (x : xs) = x : ... take 100 primzahlen takeWhile (< 100) primzahlen 80 Strictness Berechnung eines Funktions-Argumentes vor Anwendung der Funktion mitunter auch in Haskell sinnvoll (z.B. Zeit- und Platzbeschränkung der Berechnung) Syntax: f $! x berechnet denselben Wert wie f x, aber in anderer Auswertungsreihenfolge: f $! x wird erst dann ein Redex (und reduziert zu f x), sobald nach (lazy) Auswertung x kein undefinierter Wert mehr ist. Beispiel: sq $! (1 + (sq 2) ) für Funktionen mit mehreren Argumenten: Striktheit in jedem Argument einzeln. 81 Strikte Auswertung Bei bekannter Striktheit kann der Compiler mitunter effizienteren Code erzeugen (frühe Argumentauswertung) z.B. Listen-Summe mit Akkumulator: sum_n :: Integer -> [Integer] -> Integer sum_n n [] = n sum_n n ( x : xs ) = sum_n (n + x) xs sum_n 0 [ 1,2,3] sum_n 0 [ 1 .. 100000] strikte Auswertung eines Teiltermes erzwingen: sum_n n ( x : xs ) = (sum_n $!(n + x)) xs strikte Version von foldl sfoldl :: (a -> b -> a) -> a -> [b] -> a sfoldl f x [] = x sfoldl f x (y : ys) = ((sfoldl f) $! (f x y)) ys 82