Kapitel 6: Abstraktion Programmieren in Haskell 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]] Programmieren in Haskell 2 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..] Programmieren in Haskell 2 Noch ein winziger Nachtrag concat’ xs = [a | x <- xs, a <- x] Programmieren in Haskell 3 8-Damen-Problem 4 Q 3 Q [2,4,1,3] 2 Q 1 Q 1 2 3 4 Repräsentation: Liste der Zeilenpositionen, von links nach rechts Programmieren in Haskell 4 Diagonalen, aufsteigend und absteigend Programmieren in Haskell 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 5 Diagonalen, aufsteigend und absteigend 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 Die Nummer der aufsteigenden Diagonale ergibt sich als Differenz von Spalten- und Reihenposition, die Nummer der absteigenden Diagonale entsprechend durch die Summe. Programmieren in Haskell 5 • aufsteigend: gleiche Diagonale durch: j → j + 1, i → i + 1 dann: d → (j + 1) − (i + 1) = j − i oder: j → j − 1, i → i − 1 dann: d → (j − 1) − (i − 1) = j − i Programmieren in Haskell 6 • aufsteigend: gleiche Diagonale durch: j → j + 1, i → i + 1 dann: d → (j + 1) − (i + 1) = j − i oder: j → j − 1, i → i − 1 dann: d → (j − 1) − (i − 1) = j − i • absteigend: gleiche Diagonale durch: j → j + 1, i → i − 1 dann: d → (j + 1) + (i − 1) = j + i oder: j → j − 1, i → i + 1 dann: d → (j − 1) − (i + 1) = j + i Programmieren in Haskell 6 type Board = [Integer] queens :: Integer -> [Board] queens n = place 1 [1..n] [] [] Die Hilfsfunktion place erhält vier Argumente: die Nummer der aktuellen Spalte, die Liste der noch nicht verwendeten Reihenpositionen und die Nummern der bereits belegten auf- und absteigenden Diagonalen. Programmieren in Haskell 7 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 Programmieren in Haskell 8 Semantik von Listenbeschreibungen Programmieren in Haskell [ 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 ] 9 Funktionen als Parameter data Person = Person Sex FirstName LastName Date deriving (Eq,Ord,Show) data Date = Date Int Int Int deriving (Eq,Ord,Show) data Sex = Female | Male deriving (Eq,Ord,Show) type FirstName = String type LastName = String Programmieren in Haskell 10 Funktionen als Parameter data Person = Person Sex FirstName LastName Date deriving (Eq,Ord,Show) data Date = Date Int Int Int deriving (Eq,Ord,Show) data Sex = Female | Male deriving (Eq,Ord,Show) type FirstName = String type LastName = String Da lexikographisch verglichen wird, werden Elemente vom Typ Person zuerst nach dem Geschlecht, dann nach dem Vornamen, dem Nachnamen und dem Geburtsdatum angeordnet. Programmieren in Haskell 10 Verallgemeinerung von isort 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 Programmieren in Haskell 11 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 Programmieren in Haskell 12 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) Programmieren in Haskell 13 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) Programmieren in Haskell 14 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) Programmieren in Haskell 15 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) Programmieren in Haskell 15 Wörterbuch englishGerman :: Array Int (String, String) englishGerman = listArray (1,2) [("daffodil", "Narzisse"), ("dandelion","Loewenzahn")] Programmieren in Haskell 16 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) Programmieren in Haskell 16 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) Programmieren in Haskell 17 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. Programmieren in Haskell 18 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) Programmieren in Haskell -- base -- extend 19 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) Programmieren in Haskell 20 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]] Programmieren in Haskell 21 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] Programmieren in Haskell 21 Eine Variante der sort-Spezifikation sort :: (Ord a) => [a] -> [a] sort x = head [s | s <- perms x, ordered s] Programmieren in Haskell 22 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. Programmieren in Haskell 23 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 Programmieren in Haskell easy solve divide conquer <- divide x] 23 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) Programmieren in Haskell 24 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) Programmieren in Haskell 25 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 Programmieren in Haskell 26 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 Programmieren in Haskell 26 sum’ product’ and’ or’ = = = = foldr foldr foldr foldr (+) 0 (*) 1 (&&) True (||) False (:) -> (*) [] -> e a1 :(a2 :· · ·:(an−1 :(an :[]))· · ·) -> a1 *(a2 *· · ·:(an−1 *(an :e))· · ·) Programmieren in Haskell 27 Noch ein paar Beispiele isort length x ++ y reverse concat = = = = = Programmieren in Haskell foldr foldr foldr foldr foldr insert [] (\n _ -> n + 1) 0 (:) y x (\a x -> x ++ [a]) [] (++) [] 28 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 Programmieren in Haskell 29 foldr und foldl * / \ a1 * / \ foldr (*) e a2 .. <-----------\ * / \ an e Programmieren in Haskell : / \ a1 : / \ foldl (*) e a2 .. ------------> \ : / \ an [] * / \ * an / .. / * / \ e a1 30 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. Programmieren in Haskell 30 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) Programmieren in Haskell 31 reverse’’’’ = foldl (flip (:)) [] sum’’’ = foldl (+) 0 or’’’ = foldl (||) False concat’’’ = foldl (++) [] Programmieren in Haskell 32 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 Programmieren in Haskell 33 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 Programmieren in Haskell 34 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 Programmieren in Haskell 35 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 Programmieren in Haskell 35 foldm : * / \ / \ a1 : * * / \ foldm (*) e / \ / \ a2 .. ------------> .. .. .. .. \ / \ / \ : * * ... * * / \ / \ / \ / \ / \ an [] a1 a2 ai-1 ai ai+1 ai+2 an-1 an Programmieren in Haskell 36 foldm :: (a foldm (*) e foldm (*) e where f Programmieren in Haskell -> 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) x) ‘div‘ 2 m x (n-m) y 37 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 []") Programmieren in Haskell x) ‘div‘ 2 m x (n-m) y 37 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 (<=) Programmieren in Haskell 38 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) Programmieren in Haskell 39 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’ Programmieren in Haskell = = = = foldTree foldTree foldTree foldTree (\a -> 1) (+) (\a -> 0) (\m n -> max m n + 1) (\a -> [a]) merge id merge 39 map und Kolleginnen map :: (a -> b) -> [a] -> [b] map f [] = [] map f (a:x) = f a:map f x Programmieren in Haskell 40 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]] Programmieren in Haskell 41 (<*>):: (Num a) => a -> Vector a -> Vector a k <*> x = map (\a -> k*a) x Programmieren in Haskell 42 (<*>):: (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 _ _ _ = [] Programmieren in Haskell 42 (<*>):: (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) Programmieren in Haskell 42 zip :: [a] -> [b] -> [(a,b)] zip = zipWith (\a b -> (a,b)) Programmieren in Haskell 43 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] Programmieren in Haskell 43 (<++>) :: (Num a) => Matrix a -> Matrix a -> Matrix a a <++> b = zipWith (<+>) a b Programmieren in Haskell 44 (<++>) :: (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] Programmieren in Haskell 44 Induktionsschritt transpose a11 a12 ··· a1n a11 a12 a1n a2n a21 a22 a2n a21 a22 a31 .. . a32 .. . a3n .. . a31 .. . a32 .. . am1 am2 amn am1 am2 Programmieren in Haskell ··· ··· a3n .. . amn 45 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 []) Programmieren in Haskell 45 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"] Programmieren in Haskell 46 Ein paar polymorphe Funktionen ... head tail leaves build map :: :: :: :: :: Programmieren in Haskell [a] -> a [a] -> [a] Tree a -> [a] [a] -> Tree a (a -> b) -> ([a] -> [b]) 47 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 Programmieren in Haskell 48 Typklasse Ord isort :: (Ord a) => [a] -> [a] Programmieren in Haskell 49 Typklasse Ord isort :: (Ord a) => [a] -> [a] Ord Integer Ord Char Ord a ∧ Ord b ⇒ Ord (a, b) Ord a ⇒ Ord [a] Programmieren in Haskell 49 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 (<=)! Programmieren in Haskell 50 Die Typklasse Show show :: (Show a) => a -> String Programmieren in Haskell show 123 ⇒ "123" show [1, 2, 3] ⇒ "[1, 2, 3]" show "123" ⇒ "\"123\"" 51 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] Programmieren in Haskell 52 data Ordering = LT | EQ | GT compare :: (Ord a) => a -> a -> Ordering uniqueInsert’ a [] = [a] uniqueInsert’ a x@(b:y) = case LT EQ GT Programmieren in Haskell compare a b of -> a:x -> x -> b:uniqueInsert’ a y 53 Die Typklasse Num ((+), (-), (*)) size :: (Num n) => Tree a -> n depth :: (Num n, Ord n) => Tree a -> n Programmieren in Haskell 54 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) Programmieren in Haskell 55 Untertypklassen Eq Show / \ / Ord Num \ / \ Integral Fractional Programmieren in Haskell 56 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 Programmieren in Haskell 57 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’ = Programmieren in Haskell x*y’ == x’*y 58 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) Programmieren in Haskell 59 Beispiel Binärbäume instance (Eq Leaf a (Br l r) _ Programmieren in Haskell a) == == == => Eq (Tree a) where Leaf b = a == b Br l’ r’ = l == l’ && r == r’ _ = False 60 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 }. Programmieren in Haskell 60 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 <= < >= > Programmieren in Haskell y y y y = = = = compare compare compare compare x x x x y y y y /= == /= == GT LT LT GT 61 max x y | | min x y | | Programmieren in Haskell x >= y otherwise x <= y otherwise = = = = x y x y 62 instance Ord Rat where Rat x y <= Rat x’ y’ = x*y’ <= x’*y Programmieren in Haskell 63 instance Ord Rat where Rat x y <= Rat x’ y’ = x*y’ <= x’*y instance Nil Leaf Leaf Leaf Br _ Br _ Br l Programmieren in Haskell (Ord <= _ <= a <= _ <= _ <= _ <= r <= 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’ 63 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" Programmieren in Haskell 64 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 Programmieren in Haskell 64 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) Programmieren in Haskell 65 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) Programmieren in Haskell 65 myShowTree nil = leaf a = br l r = Programmieren in Haskell = myFoldTree nil leaf br where "Nil" "Leaf " ++ a "Br " ++ l ++ " " ++ r 66 myShowTree nil = leaf a = br l r = showTree’ showTree’ showTree’ showTree’ Programmieren in Haskell = 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) 66 showTree’’ showTree’’ showTree’’ showTree’’ Programmieren in Haskell :: Tree String -> ShowS Nil = showString "Nil" (Leaf a) = showString "Leaf " . showString a (Br l r) = showString "Br " . showTree’’ l . showChar ’ ’ . showTree’’ r 67 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 = (++) Programmieren in Haskell 67 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 Programmieren in Haskell 68 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 Programmieren in Haskell 69 instance Show Rat where showsPrec p (Rat x y) = showParen (p > 9) (showString "Rat " . showsPrec 10 x . showsPrec 10 y) Programmieren in Haskell 70 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) Programmieren in Haskell 70 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 ) Programmieren in Haskell 71 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 Programmieren in Haskell 72 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 Programmieren in Haskell = = = = = = = 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 73