Algorithmen und Programmierung 5. Aufgabenblatt Aufgabe 4.5 a) Codieren und Decodieren mit Caesar und Uncaesar: -- einfaches Codieren caesar :: Int -> String -> String caesar n xs = map ( chr.flip mod 255. (+) n. ord ) xs -- einfaches Decodieren uncaesar :: Int -> String -> String uncaesar n xs = caesar (-n) xs Testläufe: *Programme> uncaesar 10 (caesar 10 "abcdefg") "abcdefg" *Programme> uncaesar 100 (caesar 100 "abcdefg") "abcdefg" *Programme> caesar 160 "abcdefg" "\STX\ETX\EOT\ENQ\ACK\a\b" Der letzte Testlauf zeigt das Problem, dass die Zeichen nach der Codierung zwischen 0 und 31 liegen und nicht mehr druckbar sind. b) Alle Zeichen c mit 0 <= ord c < 32 bleiben unverändert. Damit der Text dekodierbar bleibt, darf kein Zeichen c mit ord c >= 32 auf ein Zeichen mit Ascii-Wert zwischen 0 und 31 abgebildet werden. Quellcode: -- kleinstes ( bzgl. des ASCII Wertes ) druckbares Zeichen lowChar = ord ' ' -- Codieren des druckbaren Bereichs caesar' n = map (chr.encryptC n) -- Hilfsfunktion, die das Zeichen zwischen 32 und 255 abbildet encryptC n c |ord c < lowChar = ord c |otherwise = shift (n+ord c) where shift k = (k-lowChar) `mod` (256-lowChar) + lowChar -- Decodieren des druckbaren Bereichs uncaesar' n = caesar' (-n) Testläufe: *Programme> caesar' 1 [chr 255] " " *Programme> uncaesar' (1) " " "\255" 1 *Programme> caesar' 160 "abcdefg" "!\"#$%&'" *Programme> uncaesar' 160 (caesar' 160 "abcdefg") "abcdefg" Aufgabe 5.1 Realisierung eines n-Bit-Addierwerks aus n Volladdierern data Bit = O | I deriving Show -- Logisches ODER bOR :: Bit -> Bit -> Bit bOR O O = O bOR _ _ = I -- Logisches UND bAND :: Bit -> Bit -> Bit bAND I I = I bAND _ _ = O -- Logisches XOR bXOR :: Bit -> Bit -> Bit bXOR O O = O bXOR I I = O bXOR _ _ = I -- Halbaddierer -- (Summe, Übertrag) bHalfAdder :: Bit -> Bit -> (Bit, Bit) bHalfAdder x y = (bXOR x y, bAND x y) -- Volladdierer -- (Summe, Übertrag) bVollAdder :: Bit -> Bit -> Bit -> (Bit, Bit) bVollAdder x y cin = (s2,cout) where (s,c) = bHalfAdder x y (s2,c2) = bHalfAdder s cin cout = bOR c2 c -- Addierwerk bAdder xs ys = reverse ( bAdderH (reverse xs) (reverse ys) O ) -- Endrekursiver Addierer -- 3ter Parameter a ist der Akkumulator mit dem Übertrag bAdderH :: [Bit] -> [Bit] -> Bit -> [Bit] bAdderH [] [] a = [a] -- letzen Übertrag speichern bAdderH (x:xs) (y:ys) cin = sout:(bAdderH xs ys cout) where ( sout , cout ) = bVollAdder x y cin Testlauf *Programme> bAdder [I,I,I,I] [O,O,O,O] [O,I,I,I,I] *Programme> bAdder [I,I,I,I] [I,I,I,I] 2 [I,I,I,I,O] *Programme> bAdder [I,I,I,I] [O,O,O,I] [I,O,O,O,O] Aufgabe 5.2 a) foo [] = [] foo [x:xs] = bar x : foo xs foo ist falsch definiert. Es gibt 2 korrekte Varianten: foo [] = [] foo [x:xs] = bar x : foo [xs] oder foo [] = [] foo (x:xs) = bar x : foo xs Der Rückgabetyp von foo ist abhängig von bar b) [(1:(2:[]))] Korrekt. Liste vom Typ [Int] *Programme> [(1:(2:[]))] [[1,2]] c) [1]:[2,3] Falsch. Der Listenkonstruktor ist nicht für [Int] -> [Int] definiert. *Programme> :t (:) (:) :: a -> [a] -> [a] d) ['a', chr 98, 'c'] Korrekt. chr98 liefert den Char 'b' Typ ist String oder [Char] *Programme> ['a', chr 98, 'c'] "abc" e) ['a','98','c'] Falsch. Chars in Haskell bestehen aus maximal einem Zeichen. '98' ist somit kein gültiger Char. *Programme> ['a','98','c'] <interactive>:1: lexical error in string/character literal 3 f) [[],[]] Korrekt. Liste von Listen. Typ ist abhängig vom Kontext [[a]] *Programme> [[],[]] [[],[]] g) [['1'], "Hallo"] Korrekt. Liste von Strings Typ ist [String] *Programme> [['1'], "Hallo"] ["1","Hallo"] h) ([1,2,3],"Foo") Korrekt. Tupel mit 2 Elementen Typ ist ([Int], String) *Programme> ([1,2,3],"Foo") ([1,2,3],"Foo") i) map f . map g Korrekt. Funktionsapplikation von g unf f auf eine Liste. Typ [a] -> [b] abhängig von f ung g j) map map Korrekt. map wendet map auf eine Liste von Funktionen an und liefert eine Liste mit map-Funktionen angewandt auf jeweils eine Funktion aus der Funktionsliste. (map map) [f,..,h] = [map f, ... , map h] Man kann mit der Liste arbeiten, indem man z.B. eine der Funktionen mit (!!) auswählt. Typ: (map map) :: [a->b] -> [[a]->[b]] *Programme> (!!) ((map map) [(^2),(^3)]) 0 [1..10] [1,4,9,16,25,36,49,64,81,100] *Programme> (!!) ((map map) [(^2),(^3)]) 1 [1..10] [1,8,27,64,125,216,343,512,729,1000] k) map . map Korrekt. 4 Allgemein gilt für den (.)-Operator: (.) f g x= f (g x) Analog mit map map: (map.map) f xs = ((map.map) f) xs = map (map f) xs D.h. es wird map mit einer Funktion f auf eine Liste von Listen xs gemappt. Typ: (map . map) :: (a->b) -> [[a]] -> [[b]] *Programme> (map . map) ord ["FU","Berlin"] [[70,85],[66,101,114,108,105,110]] l) map (map quadrat) [[1,2], [3,4,5]] Korrekt. Funktionsweise analog zu map.map Typ: [[Int]] *Programme> map (map (^2)) [[1,2], [3,4,5]] [[1,4],[9,16,25]] m) (4), (5) und (8) sind korrekt (4) (.) :: (b -> c) -> (a -> b) -> (a -> c) (5) (.) :: (c -> a) -> (b -> c) -> b -> a (8) (.) :: (a -> b) -> (c -> a) -> (c -> b) *Programme> :t (.) (.) :: (b -> c) -> (a -> b) -> a -> c Aufgabe 5.3 a) Join join xs ys = [(x,y,z)| (x,y)<-xs, (x',z)<-ys, x'==x ] Testlauf: *Programme> join [(1,2),(1,3)] [(1,1),(2,2)] [(1,2,1),(1,3,1)] b) Truthtab -- Permutationen der Wahrheitswerte args = [(x,y) | x <- [False,True], y <- [False, True]] -- Wahrheitstabelle wTab :: (String, (Bool -> Bool -> Bool)) -> (String, [(Bool,Bool,Bool)]) wTab (name,op) = (name, map op' args) where op' (x,y) = (x,y, op x y) 5 -- Mappt eine Liste von Boolschen Funktionen auf die Permutation der Wahrheitswerte truthTabs :: [(String, Bool -> Bool -> Bool)] -> [(String, [(Bool, Bool, Bool)])] truthTabs xs = map wTab xs Testlauf: *Programme> truthTabs [("and",(&&)),("or",(||))] [("and",[(False,False,False),(False,True,False),(True,False,Fal se),(True,True,True)]),("or",[(False,False,False),(False,True,T rue),(True,False,True),(True,True,True)])] Aufgabe 5.4 a) [ x | x <- xs, y <- ys ] gibt jedes Element x aus xs y mal hintereinander aus. *Programme> [ x | x <- [1..5], y <- [1..5] ] [1,1,1,1,1,2,2,2,2,2,3,3,3,3,3,4,4,4,4,4,5,5,5,5,5] [ x | y <- ys, x <- xs ] gibt y mal die Folge aller Elemente aus xs aus. *Programme> [ x | y <- [1..5], x <- [1..5] ] [1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5] Die beiden sind identisch, falls die die Länge von ys 1 oder 0 ist und/oder xs nur gleiche Elemente enthält. *Programme> [ x [1,1,1,1], y <True *Programme> [ x [1,1,1,1], y <True | y <- [1..5], x <- [1,1,1,1] ] == [ x | x <[1..5] ] | y <- [1..1], x <- [1,1,1,1] ] == [ x | x <[1..1] ] b) Alternative Definition von filter mit Hilfe von concat und map filternew p = concat . map bx where bx x | p x = [x] | otherwise = [] Testlauf: *Programme> filternew (>0) [-1,1] [1] c) Alle Tripel mit a2 + b2 = c2 pythagoras = [(x,y,z) | x <- [1..sqrt(999)], y <- [1..sqrt(999)], z <- [1..sqrt(999)], x*x+y*y==z*z] 6 Testlauf: *Programme> pythagoras [(3,4,5),(4,3,5),(5,12,13),(6,8,10),(7,24,25),(8,6,10),(8,15,17 ),(9,12,15),(9,40,41),(10,24,26),(11,60,61),(12,5,13),(12,9,15) ,(12,16,20),(12,35,37),(13,84,85),(14,48,50),(15,8,17),(15,20,2 5),(15,36,39),(16,12,20),(16,30,34),(16,63,65),(18,24,30),(18,8 0,82),(20,15,25),(20,21,29),(20,48,52),(21,20,29),(21,28,35),(2 1,72,75),(24,7,25),(24,10,26),(24,18,30),(24,32,40),(24,45,51), (24,70,74),(25,60,65),(27,36,45),(28,21,35),(28,45,53),(28,96,1 00),(30,16,34),(30,40,50),(30,72,78),(32,24,40),(32,60,68),(33, 44,55),(33,56,65),(35,12,37),(35,84,91),(36,15,39),(36,27,45),( 36,48,60),(36,77,85),(39,52,65),(39,80,89),(40,9,41),(40,30,50) ,(40,42,58),(40,75,85),(42,40,58),(42,56,70),(44,33,55),(45,24, 51),(45,28,53),(45,60,75),(48,14,50),(48,20,52),(48,36,60),(48, 55,73),(48,64,80),(51,68,85),(52,39,65),(54,72,90),(55,48,73),( 56,33,65),(56,42,70),(57,76,95),(60,11,61),(60,25,65),(60,32,68 ),(60,45,75),(60,63,87),(60,80,100),(63,16,65),(63,60,87),(64,4 8,80),(65,72,97),(68,51,85),(70,24,74),(72,21,75),(72,30,78),(7 2,54,90),(72,65,97),(75,40,85),(76,57,95),(77,36,85),...] Patrick Schäfer et al. 7