feld rund es sich, weitere ktion Abstraktion Abstraktion Abstraktion die dielohnt Konstruktion und dieNotationen Verarbeitung von Abstraktion Abstraktion Abstraktion Abstra chen. So erlaubt Haskell z.B. die Notation Folgen: Folgen:������� ������� �������Liste Listebis dereinschließlich positiven Zahlen, Zahlen,��� ��� der positiven Zahlen �� ��, , ���� der ungeraden, positiven Zahlen, ungeraden, positiven Zahlen bis��������� einschliefl lich sind jeweils vom Typ ��������� ���������. . Die untere GrenzeDie können durch beliebige Ausdr¸cke werden. Schrittweite ergibt sich als Differenz d zweiten Folgenglieds. Um Funktionen defi nieren, haben wir bis dato rekursive erwendet.Manchmal können listenverarbeitende nfacher und lesbarer mit Listenbeschreibungen �������und ���������ergibt sich daraus, einfach, dafl hier da dieSequen Listen werden. Schauen wir uns ein paar Beispiele an: sind. Sequenzen Dieseauf Instanz istsich besonders rsten hundert Quadratzahlen erh‰lt man mit ihre Repr‰sentation Listen eineindeutig entsprechen. Aus diesem Grund lohnt es sich, weitere Notationen Algorithmen und Datenstrukturen Isind Algorithme ������������������������������������� ‰ndertsich im n‰chsten Beispiel.Bin‰rb‰ume eine att einzuführen, die die Konstruktion und die Verarbeitung von strahieren wir vonwir einer speziellen Funktion und Repr‰sentation von Sequenzen: Die Verkettung ���wird durc Listenvereinfachen. So erlaubt Haskell z.B. die Notation n Liste, erhalten die Funktion ��� ���: : :�������� �������� implementiert und erfolgt (im Unterschied zu ���auf Listen) in arithmetischer Folgen: Folgen:������� ������� �������Liste Liste dereinschließlich Zahlen, Zahlen,��� ��� ��������� ���������Somit Somit ist ��������������������� Zeit..Andererseits erreichen wirImplementierung dies aufpositiven Kosten l‰ngerer Laufz �������Liste ������� Liste der positiven Zahlen bis �� ��, , ���� ���� ����. . Listenbeschreibungen ermˆglichenoft eine ���und ��. Die folgende liegt nahe: ��� ������ ������Liste Liste der ungeraden, positiven Zahlen, ��������� rze Formulierung von Programmen: Die vorde ������������������������������������������������ ����Liste ���� Liste der ungeraden, positiven Zahlen bis einschliefllich lernen wirBeherrschung gleichzeitig eine neue Programmiertechnik kenne eine gute vorausgesetzt uns viel Arbeit erspare In Abschnitt 6.3.1 werden wir sehen, dafl die Sortierfunktion Kapitel 4 Listen fast beliebiger Typen sortieren können ohne groß anstrengen müssen. Abhängig vom Typ der zu sortieren wird automatisch die entsprechende Vergleichsfunktion verd Keine Automatik ohne Nachteile,Betrachten denn nichtwir immer leistet Voreinstellung das Gewünschte. den Datenty ��������������������������������������������� ��������������������������������������������� ��������������������������������������������� �������Da lexikographisch verglichen wird, werden Elemen �������zuerst nachdem Geschlecht, dann nach dem Vo Typ dem Nachnamen und dem Geburtsdatumangeordnet. Selbst wir diezeigen Vergleichsoperation selbst defiexibel: nieren Wie könnten in Abs 6.3.2 wir wie, bleiben wir unfl können nic aufsteigend nach dem Nachnamen und ein anderes Mal abst nach dem Alter zu sortieren. Die Lösung für����zum dieses Problem i Parame einfach: Wir machen diekönnen Ordnungsrelation Sortierfunktionen; dann wir für jede Anwendung ein schneiderteOrdnungsrelation angeben. Hier ist die Verallgem von �����:������������������������������������ ��������������������������������������������� ��������������������������������������������� �����������������������������������������Die ur Funktion ������erhalten wir, wenn wir f¸r den Parameter wi vordefi nierte Ordnungsrelation einsetzen: �����sortieren wir absteigend. Listen von������������; Personen lassen si ������������ vielen unterschiedlichen Kriterien sortieren: ��������������������������������������������� ��������������������������������������������� ��������������������������������������������� ���������������������������������Die Definition d ������������������wirft eine interessante Frage auf:Wie zwei Personen mit demsich gleichen Geburtsdatum angeordnet? durchEinfügen verhält erfreulich: Die relative Anordnun Personen mit dem gleichenGeburtsdatum wird nicht verände ��und ��zwei Personen, die am gleichen Taggeboren sind, s ��������������������������������������������� ���������Sind die Personen bereits nach dem Nachnamen so bleibt diese Anordnung jegenauer Geburtsdatum erhalten. Es loh das Phänomen noch einmal zu betrachten. Bisher si davon ausgegangen, daß eine totale �Ordnung definiert; insb muß antisymmetrischsein: ���������� ����. Lassen wir wie ou Eigenschaft fallen, habenwir nur noch eine Präordnung vor Sortieralgorithmen funktionieren weiterhinì,nur die Spezifik Kapitel 4 ist unter diesen abgeschwächten Voraussetzungen eindeutig, da es mehrere Permutationen einer Liste geben ka � aufsteigend geordnet sind: Die Reihenfolge von Elementen Prof. Dr. Robert Giegerich Prof. Dr. Robert Giegeric ������ � ���ist eben nicht festgelegt. Verändert ein Sortierlagori Wir der hab Reihenfolge Elemente nicht,soEigenschaft heißt er stabil. dafl Stabilitätdieser eine wünschenswerte ist. Von �������nicht stabil. eingeführten Sortieralgorithmen ist nur �� läßt sich durch eine welche? kleine Änderung in der stabilisieren. Durch Wir wollen die Hilfsfunktion Verwendung von als Parametern noch an einem weiteren Beispiel studieren, d �������������aus Abschnitt 5.6 such Suche. Die Funktion in einem geordneten Feld.Vergleichsoperation In Analogie zu den Sortierfunktio wir die zugrundeliegende zum Paramete Aber es geht noch sehr viel allgemeiner: Betrachten wir die F nition von �������������genauer: Die Vergleichsoperatio ����������������verwendet; wird nur in dem Ausdruck Aufruf ändert sich jedoch lediglich Von daher liegt es die nah die Binäre Suche mit einer Funktion�.zu parametrisieren, ein Element von ���������abbildet. Daß wir in einem Feld nunmehr in den Hintergrund.Indem wir das Suchintervall üb beseitigen wir die erfolgreich, Abhängigkeit ganz.wir Noch eine letzte Ände ��in der Form ���� wir bei der Suche geben anderenfalls wird ��������zurückgegeben. ������������ ��������������������������������������������� ��������������������������������������������� Universität Bielefeld Universität Bielefeld Universität Bielefeld ��������������������������������������������� ��������������������������������������������� ��������Die Funktion ���������������implementiert ÑSpielì: Der Rechner fordert die Benutzerin auf, sich eine Zad ��und ��auszudenken. Der Rechner rät: Ist die gesuchte Zahl Benutzerin sagt, ob die Zahl gefunden wurde, obdie sieLösung kleiner f ist. Nach ��������� � ���Schritten weifl der Rechner Antworten nicht gemogelt wurde. Im Programm entspricht d ����derBenutzerin: Ist ��die ausgedachte Zahl, so beschrei ������������������ihr Verhalten. Liegt ��im Intervall � ergibt �������������������������gerade ������. Was es eigentlich, daß bei den Antworten gemogelt wird? Anders WelcheAnforderungen mufl ����erfüllen, damit die Binäre S vernünftig arbeitet? Nun wir erwarten, daß es höchstens ein gibt mit ����������, f¸r alle ������muß�����������gelten, ��entsprechend ����������. Die �Funktion ����unterteilt����f maximal in drei Bereiche: ����������� ������������������������ ��������Umfafl t der mittlere Bereich mehr als ein Element, f die Binäre Suche auch. In diesem Fall wird irgendein Elemen mittleren Drittel zur¸ckgegeben. Formal mufl ����lediglich a sein: ������ ������������mit Verallgemeiner Suche, so daß sie gerade das������������. mittlere Drittel als Intervall best WS 2004/2005 WS 2004/2005 WS 2004/2005 WS 2 Sortierfunktionen? Alle Sortierfunktionen sw �����ab, also klären Vergleichsoperation Typ. Dazu fragen wir sehrprinzipiell, was sic läßt?Abs Zahlen und Zeichen, klar; tion Abstraktionvergleichen Abstraktion wenn wir wissen, wie man die Komponent Auch Listen lassen sich vergleichen, wenn w Listenelemente vergleichen kˆnnen:������ �������������������������������� ����������Paare Listen werden hier angeordnet. Lexikasund verwenden die gleiche kommt vor Beginn, aber nach Anfall. Auf fa läßtsich eine entsprechende Ordnung defi n eine einzige Ausnahme: Funktionen. Vielle nicht auf die miteinander In diesem FallIdee, sollteFunktionen man sich vor Augen f¸h Korrektheitsbeweise oft nichts anderes machen. Nachdem die Korrektheit von ��� ren Iwir Algorithmen u �����gezeigt war, wußten ��������� stelle sich vor, die Gleichheit von Funktione Rechnennachgewiesen werden: Wir hätten �����zu zeig begnügt, die Korrektheit von Ausdruck ���������������in denanRechn Leidereine Illusion, wir stoßen hier die G Mechanisierbarkeit: Die Gleichheit von Funktionen ist formal unentscheidbar. Wir damit, dies festzustellen und verweisen aufb Literatur zurTheoretischen Informatik..Was ������ für den Typ der Sortierfunktionen? allgemein,wir müssen die Belegungen der T einschränken: ��������������������� �, auf denen Vergleichsopera für alle Typen sind, istein �����������ein Typ vonder �����. ���ist sogenannter �������, die A ��einschränkt. Dabei ist ����e Typvariable Relation auf Typen, eine sogenannte Typkla durch folgende Regeln beschreiben läßt (w Auswahl auf):���������������������� ����������������� �������Lies ����� ���. Wir haben oben Instanz der Typklasse ����jeweils defi niert wird. In Abhängigkei ������auf die entsprechende Vergle greift dies geschieht automatisch ohne unser Zut Nachtrag Listenbeschreibungen divisors’ :: (Integral a) => a -> [a] divisors’ n = [d | d <- [1..n], n ‘mod‘ d == 0] primes’ :: (Integral a) => [a] primes’ = [n | n <- [2..], divisors’ n == [1,n]] Algorithmen und Datenstrukturen I 1 Nachtrag Listenbeschreibungen divisors’ :: (Integral a) => a -> [a] divisors’ n = [d | d <- [1..n], n ‘mod‘ d == 0] primes’ :: (Integral a) => [a] primes’ = [n | n <- [2..], divisors’ n == [1,n]] sieve :: (Integral a) => [a] -> [a] sieve (a:x) = a:sieve [n | n <- x, n ‘mod‘ a /= 0] primes’’ :: (Integral a) => [a] primes’’ = sieve [2..] Algorithmen und Datenstrukturen I 1 Noch ein winziger Nachtrag concat’ xs = [a | x <- xs, a <- x] Algorithmen und Datenstrukturen I 2 8-Damen-Problem 4 Q 3 Q [2,4,1,3] 2 Q 1 Q 1 Algorithmen und Datenstrukturen I 2 3 4 3 8-Damen-Problem 4 Q 3 Q [2,4,1,3] 2 Q 1 Q 1 Algorithmen und Datenstrukturen I 2 3 4 4 −3 −2 −1 0 4 5 6 7 8 3 −2 −1 0 1 3 4 5 6 7 2 −1 0 1 2 2 3 4 5 6 1 0 1 2 3 1 2 3 4 5 1 2 3 4 1 2 3 4 3 type Board = [Integer] queens :: Integer -> [Board] queens n = place 1 [1..n] [] [] Algorithmen und Datenstrukturen I 4 place :: Integer -> [Integer] -> [Integer] -> [Integer] -> [Board] place c [] ud dd = [[]] place c rs ud dd = [q:qs | q <- rs, let uq = q - c, let dq = q + c, uq ‘notElem‘ ud, dq ‘notElem‘ dd, qs <- place (c+1) (delete q rs) (uq:ud) (dq:dd)] delete q (x:xs) | q == x = xs | otherwise = x:delete q xs Algorithmen und Datenstrukturen I 5 Semantik von Listenbeschreibungen Algorithmen und Datenstrukturen I [ e | p <- l ] = map (\p -> e) l [ e | b ] = if b then [e] else [] [ e | let ds ] = let ds in [e] [ e | q1 , q2 ] = concat [ [ e | q2 ] | q1 ] 6 Funktionen als Parameter data Person = Person Sex FirstName LastName Date deriving (Eq,Ord) data Date = Date Int Int Int deriving (Eq,Ord) data Sex = Female | Male deriving (Eq,Ord) type FirstName = String type LastName = String Algorithmen und Datenstrukturen I 7 isortBy :: (a -> a -> Bool) -> [a] -> [a] isortBy (<=) = isort where isort [] = [] isort (a:x) = insert a (isort x) insert a [] = [a] insert a x@(b:y) | a <= b = a:x | otherwise = b:insert a y Algorithmen und Datenstrukturen I 8 isortBy :: (a -> a -> Bool) -> [a] -> [a] isortBy (<=) = isort where isort [] = [] isort (a:x) = insert a (isort x) insert a [] = [a] insert a x@(b:y) | a <= b = a:x | otherwise = b:insert a y sortByLastName = isortBy (\p q -> components p <= components q) where components (Person s f l d) = (l,f,d) sortByDateOfBirth = isortBy (\p q -> dateOfBirth p <= dateOfBirth q) where dateOfBirth (Person _ _ _ d) = d Algorithmen und Datenstrukturen I 8 Binäre Suche 2 binarySearch :: (Ord b, Integral a, Ix a) => Array a b -> b -> Bool binarySearch a e = within (bounds a) where within (l,r) = l <= r && let m = (l + r) ‘div‘ 2 in case compare e (a!m) of LT -> within (l, m-1) EQ -> True GT -> within (m+1, r) Algorithmen und Datenstrukturen I 9 binarySearchBy’ :: (Integral a) => (a -> Ordering) -> (a,a) -> Bool binarySearchBy’ cmp = within where within (l,r) = if l > r then False else let m = (l+r) ‘div‘ 2 in case cmp m of LT -> within (l, m-1) EQ -> True GT -> within (m+1, r) Algorithmen und Datenstrukturen I 10 binarySearchBy :: (Integral a) => (a -> Ordering) -> (a,a) -> Maybe a binarySearchBy cmp = within where within (l,r) = if l > r then Nothing else let m = (l+r) ‘div‘ 2 in case cmp m of LT -> within (l, m-1) EQ -> Just m GT -> within (m+1, r) Algorithmen und Datenstrukturen I 11 binarySearchBy :: (Integral a) => (a -> Ordering) -> (a,a) -> Maybe a binarySearchBy cmp = within where within (l,r) = if l > r then Nothing else let m = (l+r) ‘div‘ 2 in case cmp m of LT -> within (l, m-1) EQ -> Just m GT -> within (m+1, r) binSearch a e = case r of Nothing -> False; Just _ -> True where r = binarySearchBy (\i -> compare e (a!i)) (bounds a) Algorithmen und Datenstrukturen I 11 Wörterbuch englishGerman :: Array Int (String, String) englishGerman = listArray (1,2) [("daffodil", "Narzisse"), ("dandelion","Loewenzahn")] Algorithmen und Datenstrukturen I 12 Wörterbuch englishGerman :: Array Int (String, String) englishGerman = listArray (1,2) [("daffodil", "Narzisse"), ("dandelion","Loewenzahn")] german :: [Char] -> Maybe [Char] german x = case r of Nothing -> Nothing Just m -> Just (snd (englishGerman!m)) where r = binarySearchBy (\i -> compare x (fst (englishGerman!i))) (bounds englishGerman) Algorithmen und Datenstrukturen I 12 Offene Suche openbinSearchBy :: (Integral a) => (a -> Ordering) -> a -> Maybe a openbinSearchBy cmp l = lessThan l where lessThan r = case cmp r of LT -> binarySearchBy cmp (l,r) EQ -> Just r GT -> lessThan (2*r) Algorithmen und Datenstrukturen I 13 Rekursionsschemata Strukturelle Rekursion auf Listen: Rekursionsbasis ([]) Das Problem wird für die leere Liste [] gelöst. Rekursionsschritt (a:x) Um das Problem für die Liste a:x zu lösen, wird nach dem gleichen Verfahren, d.h. rekursiv, zunächst eine Lösung für x bestimmt, die anschließend zu einer Lösung für a:x erweitert wird. Algorithmen und Datenstrukturen I 14 Vom Kochrezept zum Programm structuralRecursionOnLists :: solution -> (a -> [a] -> solution -> solution) -> [a] -> solution structuralRecursionOnLists base extend = rec where rec [] = base rec (a:x) = extend a x (rec x) Algorithmen und Datenstrukturen I -- base -- extend 15 Insertionsort mit sROL isort’ :: (Ord a) => [a] -> [a] isort’ = structuralRecursionOnLists [] (\a _ s -> insert’’ a s) insert’’ :: (Ord a) => a -> [a] -> [a] insert’’ a = structuralRecursionOnLists [a] (\b x s -> if a <= b then a:b:x else b:s) Algorithmen und Datenstrukturen I 16 Permutationen perms [2,6,7] == [[2,6,7],[6,2,7],[6,7,2],[2,7,6],[7,2,6],[7,6,2]] perms [6,7] == [[6,7],[7,6]] Algorithmen und Datenstrukturen I 17 Permutationen perms [2,6,7] == [[2,6,7],[6,2,7],[6,7,2],[2,7,6],[7,2,6],[7,6,2]] perms [6,7] == [[6,7],[7,6]] perms :: [a] -> [[a]] perms [] = [[]] perms (a:x) = [z | y <- perms x, z <- insertions a y] insertions :: a -> [a] -> [[a]] insertions a [] = [[a]] insertions a x@(b:y) = (a:x):[b:z | z <- insertions a y] Algorithmen und Datenstrukturen I 17 Eine Variante der sort-Spezifikation sort :: (Ord a) => [a] -> [a] sort x = head [s | s <- perms x, ordered s] Algorithmen und Datenstrukturen I 18 Divide and Conquer Ist das Problem einfach, wird es mit ad-hoc Methoden gelöst. Anderenfalls wird es in einfachere Teilprobleme aufgeteilt, diese werden nach dem gleichen Prinzip gelöst und anschließend zu einer Gesamtlösung zusammengefügt. Algorithmen und Datenstrukturen I 19 Divide and Conquer Ist das Problem einfach, wird es mit ad-hoc Methoden gelöst. Anderenfalls wird es in einfachere Teilprobleme aufgeteilt, diese werden nach dem gleichen Prinzip gelöst und anschließend zu einer Gesamtlösung zusammengefügt. divideAndConquer :: (problem -> Bool) --> (problem -> solution) --> (problem -> [problem]) --> ([solution] -> solution) --> problem -> solution divideAndConquer easy solve divide conquer = rec where rec x = if easy x then solve x else conquer [rec y | y Algorithmen und Datenstrukturen I easy solve divide conquer <- divide x] 19 Mergesort mit dAC msort’’ :: (Ord a) => [a] -> [a] msort’’ = divideAndConquer (\x -> drop 1 x == []) (\x -> x) (\x -> let k = length x ‘div‘ 2 in [take k x, drop k x]) (\[s,t] -> merge s t) Algorithmen und Datenstrukturen I 20 Quicksort mit dAC qsort :: (Ord a) => [a] -> [a] qsort = divideAndConquer (\x -> drop 1 x == []) (\x -> x) (\(a:x) -> [[ b | b <- x, b < a],[a],[b | b <- x, b >= a]]) (\[x,y,z] -> x++y++z) Algorithmen und Datenstrukturen I 21 foldr und Kolleginnen sum’’ :: (Num a) => [a] -> a sum’’ [] = 0 sum’’ (a:x) = a + sum’’ x or’’ :: [Bool] -> Bool or’’ [] = False or’’ (a:x) = a || or’’ x Algorithmen und Datenstrukturen I 22 foldr und Kolleginnen sum’’ :: (Num a) => [a] -> a sum’’ [] = 0 sum’’ (a:x) = a + sum’’ x or’’ :: [Bool] -> Bool or’’ [] = False or’’ (a:x) = a || or’’ x foldr’ :: (a -> solution -> solution) -> solution -> [a] -> solution foldr’ (*) e = f where f [] = e f (a:x) = a * f x Algorithmen und Datenstrukturen I 22 sum’ product’ and’ or’ = = = = foldr foldr foldr foldr (+) 0 (*) 1 (&&) True (||) False (:) -> (*) [] -> e a1 :(a2 :· · ·:(an−1 :(an :[]))· · ·) -> a1 *(a2 *· · ·:(an−1 *(an :e))· · ·) Algorithmen und Datenstrukturen I 23 Noch ein paar Beispiele isort length x ++ y reverse concat = = = = = foldr foldr foldr foldr foldr Algorithmen und Datenstrukturen I insert [] (\n _ -> n + 1) 0 (:) y x (\a x -> x ++ [a]) [] (++) [] 24 Was macht die folgende Funktion? mystery x = foldr (\a -> foldr (<->) [a]) [] x where a <-> (b:x) = if a <= b then a:b:x else b:a:x Algorithmen und Datenstrukturen I 25 foldr und foldl * / \ a1 * / \ foldr (*) e a2 .. <-----------\ * / \ an e Algorithmen und Datenstrukturen I : / \ a1 : / \ foldl (*) e a2 .. ------------> \ : / \ an [] * / \ * an / .. / * / \ e a1 26 foldr und foldl * / \ a1 * / \ foldr (*) e a2 .. <-----------\ * / \ an e : / \ a1 : / \ foldl (*) e a2 .. ------------> \ : / \ an [] foldr (*) e (reverse x) = * / \ * an / .. / * / \ e a1 foldl (flip (*)) e x, mit flip (*) = \a b -> b * a. Algorithmen und Datenstrukturen I 26 foldl foldl’’ :: (solution -> a -> solution) -> solution -> [a] -> solution foldl’’ (*) e x = f x e where f [] e = e f (a:x) e = f x (e*a) Algorithmen und Datenstrukturen I 27 reverse’’’’ = foldl (flip (:)) [] sum’’’ = foldl (+) 0 or’’’ = foldl (||) False concat’’’ = foldl (++) [] Algorithmen und Datenstrukturen I 28 Speicher- und Zeitbedarf sum sum’’ [1..9] sum’’’ [1..9] ⇒ f [1..9] ⇒ f’ [1..9] 0 ⇒ 1 + f [2..9] ⇒ f’ [2..9] 1 ⇒ 1 + (2 + f [3..9]) ⇒ f’ [3..9] 3 ... ... ⇒ 1 + (2 + ... + (9 + f [])) ⇒ f’ [] 45 ⇒ 1 + (2 + ... + (9 + 0)) ⇒ 45 ⇒ 45 Algorithmen und Datenstrukturen I 29 Speicher- und Zeitbedarf or or’’ [False,True,False] or’’’ [False,True,False] ⇒ f [False,True,False] ⇒ f’ [False,True,False] False ⇒ False || f [True,False] ⇒ f’ [True,False] (False || False) ⇒ f [True,False] ⇒ f’ [True,False] False ⇒ True || f [False] ⇒ f’ [False] (True || False) ⇒ True ⇒ f’ [False] True ⇒ f’ [] (False || True) ⇒ f’ [] True ⇒ True Algorithmen und Datenstrukturen I 30 foldr1 und foldl1 foldr1 :: (a -> a -> a) -> [a] -> a foldl1 :: (a -> a -> a) -> [a] -> a foldr1 (*) = f where f [a] = a f (a:b:xs) = a * f (b:xs) foldl1 (*) (x:xs) = foldl (*) x xs Algorithmen und Datenstrukturen I 31 foldr1 und foldl1 foldr1 :: (a -> a -> a) -> [a] -> a foldl1 :: (a -> a -> a) -> [a] -> a foldr1 (*) = f where f [a] = a f (a:b:xs) = a * f (b:xs) foldl1 (*) (x:xs) = foldl (*) x xs righty, lefty :: [a] -> Tree a righty = foldr1 Br . map Leaf lefty = foldl1 Br . map Leaf Algorithmen und Datenstrukturen I 31 foldm : * / \ / \ a1 : * * / \ foldm (*) e / \ / \ a2 .. ------------> .. .. .. .. \ / \ / \ : * * ... * * / \ / \ / \ / \ / \ an [] a1 a2 ai-1 ai ai+1 ai+2 an-1 an Algorithmen und Datenstrukturen I 32 foldm :: (a foldm (*) e foldm (*) e where f -> a -> a) -> a -> [a] -> a [] = e x = fst (f (length x) x) n x = if n == 1 then (head x, tail else let m = n (a,y) = f (b,z) = f in (a*b,z) Algorithmen und Datenstrukturen I x) ‘div‘ 2 m x (n-m) y 33 foldm :: (a foldm (*) e foldm (*) e where f -> a -> a) -> a -> [a] -> a [] = e x = fst (f (length x) x) n x = if n == 1 then (head x, tail else let m = n (a,y) = f (b,z) = f in (a*b,z) foldm1 (*) = foldm (*) (error "foldm1 []") Algorithmen und Datenstrukturen I x) ‘div‘ 2 m x (n-m) y 33 mergeList :: (Ord a) => [[a]] -> [a] mergeList = foldm merge [] balanced :: [a] -> Tree a balanced = foldm1 Br . map Leaf msortBy, smsortBy :: (a -> a -> Bool) -> [a] -> [a] msortBy (<=) = foldm (mergeBy (<=)) [] . map (\a -> [a]) smsortBy (<=) = foldm (mergeBy (<=)) [] . runsBy (<=) Algorithmen und Datenstrukturen I 34 Fold auf Bäumen foldTree :: (a -> b) -> (b -> b -> b) -> Tree a -> b foldTree leaf br = f where f (Leaf a) = leaf a f (Br l r) = br (f l) (f r) Algorithmen und Datenstrukturen I 35 Fold auf Bäumen foldTree :: (a -> b) -> (b -> b -> b) -> Tree a -> b foldTree leaf br = f where f (Leaf a) = leaf a f (Br l r) = br (f l) (f r) size’ depth’ mergeTree’ mergeRuns’ = = = = foldTree foldTree foldTree foldTree Algorithmen und Datenstrukturen I (\a -> 1) (+) (\a -> 0) (\m n -> max m n + 1) (\a -> [a]) merge id merge 35 map und Kolleginnen map :: (a -> b) -> [a] -> [b] map f [] = [] map f (a:x) = f a:map f x Algorithmen und Datenstrukturen I 36 Anwendung: Vektoren und Matrizen type Vector a = [a] type Matrix a = [Vector a] 3 2 8 17 0 1 [[3,8,0],[2,17,1]] Algorithmen und Datenstrukturen I 37 (<*>):: (Num a) => a -> Vector a -> Vector a k <*> x = map (\a -> k*a) x Algorithmen und Datenstrukturen I 38 (<*>):: (Num a) => a -> Vector a -> Vector a k <*> x = map (\a -> k*a) x zipWith :: (a -> b -> c) -> [a] -> [b] -> [c] zipWith f (a:x) (b:y) = f a b:zipWith f x y zipWith _ _ _ = [] Algorithmen und Datenstrukturen I 38 (<*>):: (Num a) => a -> Vector a -> Vector a k <*> x = map (\a -> k*a) x zipWith :: (a -> b -> c) -> [a] -> [b] -> [c] zipWith f (a:x) (b:y) = f a b:zipWith f x y zipWith _ _ _ = [] (<+>) :: (Num a) => Vector a -> Vector a -> Vector a x <+> y = zipWith (+) x y (<.>) :: (Num a) => Vector a -> Vector a -> a x <.> y = sum (zipWith (*) x y) Algorithmen und Datenstrukturen I 38 zip :: [a] -> [b] -> [(a,b)] zip = zipWith (\a b -> (a,b)) Algorithmen und Datenstrukturen I 39 zip :: [a] -> [b] -> [(a,b)] zip = zipWith (\a b -> (a,b)) k <*> x = [k*a | a <- x] x <+> y = [a+b | (a,b) <- zip x y] x <.> y = sum [a*b | (a,b) <- zip x y] Algorithmen und Datenstrukturen I 39 (<++>) :: (Num a) => Matrix a -> Matrix a -> Matrix a a <++> b = zipWith (<+>) a b Algorithmen und Datenstrukturen I 40 (<++>) :: (Num a) => Matrix a -> Matrix a -> Matrix a a <++> b = zipWith (<+>) a b (<**>) :: (Num a) => Matrix a -> Matrix a -> Matrix a m <**> n = [[x <.> y | y <- transpose’ n] | x <- m] Algorithmen und Datenstrukturen I 40 Induktionsschritt transpose a11 a12 ··· a1n a11 a12 a1n a2n a21 a22 a2n a21 a22 a31 .. . a32 .. . a3n .. . a31 .. . a32 .. . am1 am2 amn am1 am2 Algorithmen und Datenstrukturen I ··· ··· a3n .. . amn 41 Induktionsschritt transpose a11 a12 ··· a1n a11 a12 a1n a2n a21 a22 a2n a21 a22 a31 .. . a32 .. . a3n .. . a31 .. . a32 .. . am1 am2 amn am1 am2 ··· ··· a3n .. . amn transpose’ :: Matrix a -> Matrix a transpose’ = foldr (zipWith (:)) (repeat []) Algorithmen und Datenstrukturen I 41 Typpolymorphismus und Typklassen (++) :: [a] -> [a] -> [a] "hello " ++ "world" ⇒ "hello world" [19, 9] ++ [7] ⇒ [19, 9, 7] ["hello ","world"] ++ ["it’s","me"] ⇒ ["hello ","world","it’s","me"] Algorithmen und Datenstrukturen I 42 Ein paar polymorphe Funktionen ... head tail leaves build map :: :: :: :: :: [a] -> a [a] -> [a] Tree a -> [a] [a] -> Tree a (a -> b) -> ([a] -> [b]) Algorithmen und Datenstrukturen I 43 Vergleichsoperator (<=) (a,b) <= (c,d) = a < c || a == c && b <= d [] <= x = True (a:x) <= [] = False (a:x) <= (b:y) = a < b || a == b && x <= y Algorithmen und Datenstrukturen I 44 Typklasse Ord isort :: (Ord a) => [a] -> [a] Algorithmen und Datenstrukturen I 45 Typklasse Ord isort :: (Ord a) => [a] -> [a] Ord Integer Ord Char Ord a ∧ Ord b ⇒ Ord (a, b) Ord a ⇒ Ord [a] Algorithmen und Datenstrukturen I 45 isort ["Beginn","Anfall","Anfang"] ⇒ ["Anfall","Anfang","Beginn"] isort [(3,1),(1,7),(1,3),(2,2)] ⇒ [(1,3),(1,7),(2,2),(3,1)] Verschiedene (<=)! Algorithmen und Datenstrukturen I 46 Die Typklasse Show show :: (Show a) => a -> String Algorithmen und Datenstrukturen I show 123 ⇒ "123" show [1, 2, 3] ⇒ "[1, 2, 3]" show "123" ⇒ "\"123\"" 47 Eq und Ord unique :: (Eq a) => [a] -> [a] element :: (Eq a) => a -> [a] -> Bool insert :: (Ord a) => a -> [a] -> [a] merge :: (Ord a) => [a] -> [a] -> [a] Algorithmen und Datenstrukturen I 48 data Ordering = LT | EQ | GT compare :: (Ord a) => a -> a -> Ordering uniqueInsert’ a [] = [a] uniqueInsert’ a x@(b:y) = case LT EQ GT Algorithmen und Datenstrukturen I compare a b of -> a:x -> x -> b:uniqueInsert’ a y 49 Die Typklasse Num ((+), (-), (*)) size :: (Num n) => Tree a -> n depth :: (Num n, Ord n) => Tree a -> n Algorithmen und Datenstrukturen I 50 Integral und Fractional power :: (Num a, Integral b) => a -> b -> a power x n | n == 0 = 1 | n ‘mod‘ 2 == 0 = y | otherwise = y*x where y = power (x*x) (n ‘div‘ 2) Algorithmen und Datenstrukturen I 51 Untertypklassen Eq Show / \ / Ord Num \ / \ Integral Fractional Algorithmen und Datenstrukturen I 52 Beispiel rationale Zahlen, (==) data Rat = Rat Int Int Falsche Definition (Rat 67 18 /= Rat 8241 2214): Rat x y == Rat x’ y’ = x == x’ && y == y’ Richtige Definition: Rat x y == Rat x’ y’ = x*y’ == x’*y Algorithmen und Datenstrukturen I 53 class- und instance-Deklarationen (Eq und Ord) class Eq a where (==), (/=) :: a -> a -> Bool x /= y = not (x == y) instance Eq Rat where Rat x y == Rat x’ y’ = Algorithmen und Datenstrukturen I x*y’ == x’*y 54 Rat mit smart-Konstruktor rat :: Int -> Int -> Rat rat x y = norm (x * signum y) (abs y) where norm x y = let d = gcd x y in Rat (x ‘div‘ d) (y ‘div‘ d) Algorithmen und Datenstrukturen I 55 Beispiel Binärbäume instance (Eq Leaf a (Br l r) _ a) == == == Algorithmen und Datenstrukturen I => Eq (Tree a) where Leaf b = a == b Br l’ r’ = l == l’ && r == r’ _ = False 56 Beispiel Binärbäume instance (Eq Leaf a (Br l r) _ a) == == == => Eq (Tree a) where Leaf b = a == b Br l’ r’ = l == l’ && r == r’ _ = False Allgemeine Form einer Instanzdeklaration: instance (C1 β1 ,...,Cm βm ) => C(T α1 . . . αn ) mit {β1 , . . . , βm } ⊆ {α1 , . . . , αn }. Algorithmen und Datenstrukturen I 56 Ord als Untertypklasse von Eq class (Eq a) => Ord a where compare :: a -> a -> Ordering (<), (<=), (>=), (>) :: a -> a -> Bool max, min :: a -> a -> a compare x y | x == y = EQ | x <= y = LT | otherwise = GT x x x x <= < >= > y y y y = = = = compare compare compare compare Algorithmen und Datenstrukturen I x x x x y y y y /= == /= == GT LT LT GT 57 max x y | | min x y | | Algorithmen und Datenstrukturen I x >= y otherwise x <= y otherwise = = = = x y x y 58 instance Ord Rat where Rat x y <= Rat x’ y’ = x*y’ <= x’*y Algorithmen und Datenstrukturen I 59 instance Ord Rat where Rat x y <= Rat x’ y’ = x*y’ <= x’*y instance Nil Leaf Leaf Leaf Br _ Br _ Br l (Ord <= _ <= a <= _ <= _ <= _ <= r <= Algorithmen und Datenstrukturen I a) => Ord (Tree a) where t = True Nil = False Leaf b = a <= b Br _ _ = True Nil = False Leaf _ = False Br l’ r’ = l < l’ || l == l’ && r <= r’ 59 Ausgabe eines Baums showTree showTree showTree showTree :: Tree String -> String Nil = "Nil" (Leaf a) = "Leaf " ++ a (Br l r) = "Br " ++ showTree l ++ " " ++ showTree r showTree (Br (Br (Leaf "a") (Leaf "b")) Nil) => "Br Br Leaf a Leaf b Nil" Algorithmen und Datenstrukturen I 60 Ausgabe eines Baums showTree showTree showTree showTree :: Tree String -> String Nil = "Nil" (Leaf a) = "Leaf " ++ a (Br l r) = "Br " ++ showTree l ++ " " ++ showTree r showTree (Br (Br (Leaf "a") (Leaf "b")) Nil) => "Br Br Leaf a Leaf b Nil" leaves leaves leaves leaves :: Tree a -> [a] Nil = [] (Leaf a) = [a] (Br l r) = leaves l ++ leaves r Algorithmen und Datenstrukturen I 60 foldTree :: (a -> b) -> (b -> b -> b) -> Tree a -> b foldTree leaf br = f where f (Leaf a) = leaf a f (Br l r) = br (f l) (f r) Algorithmen und Datenstrukturen I 61 foldTree :: (a -> b) -> (b -> b -> b) -> Tree a -> b foldTree leaf br = f where f (Leaf a) = leaf a f (Br l r) = br (f l) (f r) myFoldTree :: b -> (a -> b) -> (b -> b -> b) -> Tree a -> b myFoldTree nil leaf br = f where f Nil = nil f (Leaf a) = leaf a f (Br l r) = br (f l) (f r) Algorithmen und Datenstrukturen I 61 myShowTree nil = leaf a = br l r = = myFoldTree nil leaf br where "Nil" "Leaf " ++ a "Br " ++ l ++ " " ++ r Algorithmen und Datenstrukturen I 62 myShowTree nil = leaf a = br l r = showTree’ showTree’ showTree’ showTree’ = myFoldTree nil leaf br where "Nil" "Leaf " ++ a "Br " ++ l ++ " " ++ r :: Tree String -> String -> String Nil s = "Nil" ++ s (Leaf a) s = "Leaf " ++ a ++ s (Br l r) s = "Br " ++ showTree’ l (" " ++ showTree’ r s) Algorithmen und Datenstrukturen I 62 showTree’’ showTree’’ showTree’’ showTree’’ :: Tree String -> ShowS Nil = showString "Nil" (Leaf a) = showString "Leaf " . showString a (Br l r) = showString "Br " . showTree’’ l . showChar ’ ’ . showTree’’ r Algorithmen und Datenstrukturen I 63 showTree’’ showTree’’ showTree’’ showTree’’ :: Tree String -> ShowS Nil = showString "Nil" (Leaf a) = showString "Leaf " . showString a (Br l r) = showString "Br " . showTree’’ l . showChar ’ ’ . showTree’’ r type ShowS = String -> String showChar :: Char -> ShowS showChar = (:) showString :: String -> ShowS showString = (++) Algorithmen und Datenstrukturen I 63 Die Typklasse Show class Show a where showsPrec :: Int -> a -> ShowS showList :: [a] -> ShowS showList [] = showString "[]" showList (a:x) = showChar ’[’ . shows a . showRest x where showRest [] = showChar ’]’ showRest (a:x) = showString ", " . shows a . showRest x Algorithmen und Datenstrukturen I 64 shows :: (Show a) => a -> ShowS shows = showsPrec 0 show :: (Show a) => a -> String show x = shows x "" showParen :: Bool -> ShowS -> ShowS showParen b p = if b then showChar ’(’ . p . showChar ’)’ else p Algorithmen und Datenstrukturen I 65 instance Show Rat where showsPrec p (Rat x y) = showParen (p > 9) (showString "Rat " . showsPrec 10 x . showsPrec 10 y) Algorithmen und Datenstrukturen I 66 instance Show Rat where showsPrec p (Rat x y) = showParen (p > 9) (showString "Rat " . showsPrec 10 x . showsPrec 10 y) instance Show Rat where showsPrec p (Rat x y) = showParen (p > 9) (showString "Rat " . showsPrec 10 x . showChar ’ ’ . showsPrec 10 y) Algorithmen und Datenstrukturen I 66 instance (Show a) => Show (Tree a) where showsPrec p Nil = showString "Nil" showsPrec p (Leaf a) = showParen (p > 9) (showString "Leaf " . showsPrec 10 a) showsPrec p (Br l r) = showParen (p > 9) (showString "Br " . showsPrec 10 l . showChar ’ ’ . showsPrec 10 r ) Algorithmen und Datenstrukturen I 67 Die Typklasse Num class (Eq a, Show (+), (-), (*) negate abs, signum fromInteger a) :: :: :: :: => Num a where a -> a -> a a -> a a -> a Integer -> a x-y = x + negate y Algorithmen und Datenstrukturen I 68 instance Num Rat where Rat x y + Rat x’ y’ Rat x y * Rat x’ y’ negate (Rat x y) abs (Rat x y) signum (Rat x y) fromInteger x fromInt x Algorithmen und Datenstrukturen I = = = = = = = Rat Rat Rat Rat Rat Rat Rat (x*y’ + x’*y) (y*y’) (x*x’) (y*y’) (negate x) y (abs x) (abs y) (signum (x*y)) 1 (fromInteger x) 1 (fromInt x) 1 -- Hugs-spezifisch 69