Grundlagen der Programmierung 2 (1.D) Prof. Dr. Manfred Schmidt-Schauÿ Künstliche Intelligenz und Softwaretechnologie 8. Mai 2007 Fallunterscheidung if-then-else Die Definition mein_if x y z = case x of True -> y; False -> z ist äquivalent zum if . then . else Grundlagen der Programmierung 2 (1.D) - 1 - Reduktionsregel zum case case-Reduktion (case (c t1 . . . tn) of . . . (c x1 . . . xn → s) . . .) s[t1/x1, . . . , tn/xn] (mit sharing) (case (c t1 . . . tn) of . . . (c x1 . . . xn → s) . . .) let {x1 = t1; . . . ; xn = tn} in s Grundlagen der Programmierung 2 (1.D) - 2 - Funktionsdefinitionen: Strategie Fallunterscheidungen mit Mustern in Funktionsdefinitionen: können als case-Ausdrücke geschrieben werden. Haskell erlaubt überlappende Muster Auswertungs-Strategie in Funktionsdefinitionen mit Mustern und Wächtern: Muster von oben nach unten, bis Muster passt und Wächter = True Es gilt: Diese Strategie ist mittels geschachtelter case-Ausdrücke definierbar Grundlagen der Programmierung 2 (1.D) - 3 - Auswertung in Haskell Kombination von Transformation und Auswertung: Haskell- Programm Entzuckerung Programm in Kernsprache Syntaxanalyse Syntaxbaum des Programms Auswertung (operationelle Semantik) transformierter Syntaxbaum Grundlagen der Programmierung 2 (1.D) - 4 - Entzuckerung: Beispiel map f [] map f (x:xs) = [] = f x : map f xs kann man transformieren zu: map f lst = (case lst of [] -> []; (x:xs) -> f x : map f xs) (Nach einer Programmanalyse) Grundlagen der Programmierung 2 (1.D) - 5 - Bemerkungen zu: Normaler Reihenfolge der Auswertung • Normale Reihenfolge der Auswertung ist erweitert um neue Reduktion: case-Reduktion • Normale Reihenfolge der Auswertung reduziert nicht unter Konstruktoren (auch nicht in Abstraktionen) Dadurch kann man (potentiell) unendliche Listen verarbeiten Grundlagen der Programmierung 2 (1.D) - 6 - Listenausdrücke, Listen-Komprehensionen; (list comprehensions) Analog zu Mengenausdrücken, aber Reihenfolge der Listenelemente im Resultat wird festgelegt. Beispiele: [x | [f x | [x | [(x,y) x x x | <- xs] <- xs] <- xs, p x] x <- xs, y <-ys] [y | x <- xs, y <-x] entspricht entspricht entspricht entspricht entspricht xs map f xs filter p xs xs "kreuzprodukt" ys fuer endliche Listen concat Syntax: [hAusdrucki ”|” hGeneratori | hFilteri{, {hGeneratori | hFilteri}}∗]. Grundlagen der Programmierung 2 (1.D) - 7 - Listen-Komprehensionen [Resultatausdruck | Generatoren,Testfunktionen] Generator: Prädikat: v <- liste Test auf True/False. Wirkungsweise: generiert Elemente der Liste Element wird akzeptiert/nicht akz. die Generatoren liefern nach und nach die Elemente der Listen. Wenn alle Prädikate zutreffen, wird Resultatausdruck in die Resultatliste aufgenommen. neue lokale Variablen sind möglich deren Geltungsbereich ist rechts von der Einführung Grundlagen der Programmierung 2 (1.D) - 8 - Listen-Komprehensionen: Beispiel [(x,y) | x <- [1..10], even x, y <- [2..6], x < y] Resultat: [(2,3),(2,4),(2,5),(2,6),(4,5),(4,6)] Begründung: Erzeugungsreihenfolge: x y ? 1 N 2 2 N 2 3 Y 2 4 Y 2 5 Y Grundlagen der Programmierung 2 (1.D) 2 6 Y 3 N 4 2 N 4 3 N 4 4 N 4 5 Y 4 6 Y 5 N 6 2 N ... ... ... - 9 - Listen-Komprehensionen: Beispiel [(x,y) | x <- [1..10], y <- [1..x]] [(1,1), (2,1),(2,2), (3,1),(3,2),(3,3), (4,1),(4,2),(4,3),(4,4), (5,1),(5,2),(5,3),(5,4),(5,5), (6,1),(6,2),(6,3),(6,4),(6,5),(6,6), (7,1),(7,2),(7,3),(7,4),(7,5),(7,6),(7,7), (8,1),(8,2),(8,3),(8,4),(8,5),(8,6),(8,7),(8,8), (9,1),(9,2),(9,3),(9,4),(9,5),(9,6),(9,7),(9,8),(9,9), (10,1),(10,2),(10,3),(10,4),(10,5),(10,6),(10,7),(10,8),(10,9),(10,10)] Grundlagen der Programmierung 2 (1.D) - 10 - Listen-Komprehensionen: Beispiel Liste der nicht durch 2,3,5 teilbaren Zahlen. Die erste Nicht-Primzahl darin ist 49: > [x | x <- [2..], x ‘rem‘ 2 /= 0, x ‘rem‘ 3 /= 0, x ‘rem‘ 5 /= 0] [7,11,13,17,19,23,29,31,37,41,43,47,49,53,59,61,67,71,73,77,79,83,89,91,.. Grundlagen der Programmierung 2 (1.D) - 11 - Listen-Komprehensionen: Beispiel Primzahlen primes = 2: [x | x <- [3,5..], and (map (\t-> x ‘mod‘ t /= 0) (takeWhile (\y -> y^2 <= x) primes))] Grundlagen der Programmierung 2 (1.D) - 12 - Optimierungen: am Beispiel foldl foldl :: (a -> b -> a) -> a -> [b] -> a foldl f z [] = z foldl f z (x:xs) = foldl f (f z x) xs foldl’ :: (a -> b -> a) -> a -> [b] -> a foldl’ f a [] = a foldl’ f a (x:xs) = ((foldl’ f) $! (f a x)) xs fun $! x entspricht: Grundlagen der Programmierung 2 (1.D) Zuerst x auswerten, dann fun x - 13 - Ressourcenbedarf von foldl, foldr, foldl’ foldl und foldr haben unterschiedlichen Ressourcenbedarf in Abhängigkeit vom Operator foldl’ ist speicher-optimiertes foldl (abh. von Operator) foldr (++) [] xs ist schneller als foldl (++) [] xs foldl’ (+) 0 xs braucht weniger Platz als foldr (+) 0 xs Main> length (foldr (++) [] [[x] | x <- [1..1000]]) 1000 :: Int (29036 reductions, 43060 cells) Main> length (foldl (++) [] [[x] | x <- [1..1000]]) 1000 :: Int (527532 reductions, 1539550 cells, 6 garbage collections) Grundlagen der Programmierung 2 (1.D) - 14 - Weitere Listen-Funktionen Umdrehen einer Liste: reverse_naiv [] reverse_naiv (x:xs) = [] = (reverse_naiv xs) ++ [x] effizientes Umdrehen einer Liste (iterativer Prozess) reverse xs = foldl (\x y -> y:x) [] xs Grundlagen der Programmierung 2 (1.D) - 15 - Weitere Listen-Funktionen -- Restliste nach n-tem Element drop 0 xs = xs drop _ [] = [] drop n (_:xs) | n>0 = drop (n-1) xs drop _ _ = error "Prelude.drop: negative argument" -- Bildet Liste der zip :: zip (a:as) (b:bs) zip _ _ Paare [a] -> [b] -> [(a,b)] = (a,b) : zip as bs = [] --- aus Liste von Paaren ein Paar von Listen unzip :: [(a,b)] -> ([a],[b]) unzip = foldr (\(a,b) (as,bs) -> (a:as, b:bs)) ([], []) Grundlagen der Programmierung 2 (1.D) - 16 - Beispiele drop 10 [1..100] -----> zip "abcde" [1..] -----> [11,12,... [(’a’,1),(’b’,2),(’c’,3),(’d’,4),(’e’,5)] unzip (zip "abcdefg" [1..]) ----> ("abcdefg",[1,2,3,4,5,6,7]) Grundlagen der Programmierung 2 (1.D) - 17 - Ressourcenbedarf von Listenfunktionen Drei Möglichkeiten der Auswertung: Komplett-Auswertung z.B. beim Drucken der Ergebnisliste im Interpreter Rückgrat-Auswertung nur soviel, wie length von der Ergebnisliste benötigt. Kopf-Auswertung (einfache. Ausw.) nur soviel, dass die Frage Ist die Ergebnisliste leer oder nicht leer“ beantwortet werden kann. ” Grundlagen der Programmierung 2 (1.D) - 18 - Ressourcenbedarf von Append xs,ys seien ausgewertete Listen der Länge m und n. Bei Rückgrat-Auswertung: xs ++ ys benötigt O(m + n) Reduktionsschritte O(m) Platz: nur das Rückgrat muss kopiert werden Grundlagen der Programmierung 2 (1.D) - 19 - Ressourcenbedarf von map xs sei ausgewertete Liste der Länge n map f xs: Rückgratauswertung: Komplett-Auswertung: O(n) Reduktionsschritte # Reduktionsschritte abhängig von f Rückgratauswertung: Komplett-Auswertung: O(n) Speicherbedarf Speicherbedarf abhängig von f Grundlagen der Programmierung 2 (1.D) - 20 - Ressourcenbedarf von concat Sei xs ausgewertete Liste von Listen der Länge m, das i-te Element sei eine Liste mit ni Elementen. concat xs bei Rückgratauswertung Reduktionsschritte: n1 + n2 + . . . nm + m Platzbedarf: O(n1 + . . . + nm−1 + m) letzte Liste muss nicht kopiert werden Grundlagen der Programmierung 2 (1.D) - 21 -