Prof.aa Dr. J. Giesl Programmierung WS12/13 Tutoriumslösung - Übung 10 (Abgabe 11.01.2013) M. Brockschmidt, F. Emmes, C. Otto, T. Ströder Tutoraufgabe 1 (Auswertungsstrategie): Gegeben sei das folgende Haskell-Programm: absteigend :: Int -> [ Int ] absteigend 0 = [] absteigend n = n : absteigend (n -1) produkt :: [ Int ] -> Int produkt [] = 1 produkt ( x : xs ) = x * produkt xs summe :: [ Int ] -> Int summe xs = summe ’ xs 0 where summe ’ [] a = a summe ’ ( x : xs ) a = summe ’ xs ( a + x ) Die Funktion absteigend berechnet die absteigende Liste der natürlichen Zahlen bis hinunter zu 1. Zum Beispiel berechnet absteigend 5 die Liste [5,4,3,2,1]. Die Funktion produkt multipliziert die Elemente einer Liste, beispielsweise ergibt produkt [3,5,2,1] die Zahl 30. Die Funktion summe addiert die Elemente einer Liste. Zum Beispiel liefert summe [5,2,7] den Wert 14. Geben Sie alle Zwischenschritte bei der Auswertung der folgenden Ausdrücke an: 1. produkt (absteigend 2) 2. summe (absteigend 2) Beachten Sie, dass Haskell eine Leftmost-Outermost Auswertungsstrategie besitzt. Allerdings sind Operatoren wie * und +, die auf eingebauten Zahlen arbeiten, strikt, d.h. hier müssen vor Anwendung des Operators seine Argumente vollständig ausgewertet worden sein. Lösung: produkt (absteigend 2) → produkt (2 : absteigend (2-1)) → 2 * produkt (absteigend (2-1)) → 2 * produkt (absteigend 1) → 2 * produkt (1 : absteigend (1-1)) → 2 * (1 * produkt (absteigend (1-1))) → 2 * (1 * produkt (absteigend 0))) → 2 * (1 * produkt []) → 2 * (1 * 1) →2 * 1 →2 summe (absteigend 2) → summe’ (absteigend 2) 0 → summe’ (2 : absteigend (2-1)) 0 → summe’ (absteigend (2-1)) (0+2) → summe’ (absteigend 1) (0+2) → summe’ (1 : absteigend (1-1)) (0+2) → summe’ (absteigend (1-1)) ((0+2)+1) → summe’ (absteigend 0) ((0+2)+1) → summe’ [] ((0+2)+1) → (0+2)+1 1 Programmierung WS12/13 Tutoriumslösung - Übung 10 (Abgabe 11.01.2013) → 2+1 →3 Tutoraufgabe 3 (Listen): Seien x, y ganze Zahlen vom Typ Int und xs und ys Listen der Längen n und m vom Typ [Int]. Welche der folgenden Gleichungen zwischen Listen sind richtig und welche nicht? Begründen Sie Ihre Antwort. Falls es sich um syntaktisch korrekte Ausdrücke handelt, geben Sie für jede linke und rechte Seite auch an, wie viele Elemente in der jeweiligen Liste enthalten sind und welchen Typ sie hat. Beispiel : Die Liste [[1,2,3],[4,5]] hat den Typ [[Int]] und enthält 2 Elemente. Hinweise: • Hierbei steht ++ für den Verkettungsoperator für Listen. Das Resultat von xs ++ ys ist die Liste, die entsteht, wenn die Elemente aus ys — in der Reihenfolge wie sie in ys stehen — an das Ende von xs angefügt werden. Beispiel : [1,2] ++ [1,2,3] = [1,2,1,2,3] • Falls linke und rechte Seite gleich sind, genügt eine Angabe des Typs und der Elementzahl a) x:xs = [x] ++ xs b) (x:y):xs = x:y:xs c) [x,y,xs] = x:y:xs d) x:y:((x:[x]) ++ xs) = [x,y,x] ++ (x:xs) e) []:[[],[[1]]] = [[],[]]:[[[1]]] Lösung: a) Beide Ausdrücke repräsentieren die gleichen Listen der Länge n + 1 und vom Typ [Int]. b) Der erste Ausdruck ist nicht typkorrekt, da im Teilausdruck x:y die Variable y keine Liste ist. Sie kann somit nicht als zweites Argument des Konstruktors “:” verwendet werden. Der rechte Ausdruck ist typkorrekt und repräsentiert eine Liste der Länge n + 2 vom Typ [Int]. Die Ausdrücke sind also nicht gleich. c) Der linke Ausdruck ist nicht typkorrekt, da x und y nicht den gleichen Typ wie xs haben, aber beide in der gleichen Liste enthalten sind. Der rechte Ausdruck repräsentiert eine Liste der Länge n + 2 und ist vom Typ [Int]. Die Ausdrücke sind also nicht gleich. d) Beides sind typkorrekte Listenausdrücke (äquivalent zu [x,y,x,x] ++ xs der Länge n + 4 und ebenfalls vom Typ [Int]. Die Ausdrücke sind also gleich. e) Der linke Ausdruck ergibt ausgeschrieben die Liste [[],[],[[1]]], also eine dreielementige Liste, die Listen von Listen enthält. Der genaue Typ der innersten Liste ist [Int]. Der Typ des gesamten Ausdrucks ist demnach [[[Int]]]. Der zweite Ausdruck ergibt ausgeschrieben die Liste [[[],[]],[[1]]], also eine zweielementige Liste, die Listen von Listen enthält. Auch hier ist der Typ der innersten Liste [Int] und der Typ des gesamten Ausdrucks ist [[[Int]]]. Da die beiden Ausdrucke zwar den gleichen Typ haben, aber nicht die gleichen Listen darstellen, sind die Ausdrücke also nicht gleich. 2 Programmierung WS12/13 Tutoriumslösung - Übung 10 (Abgabe 11.01.2013) Tutoraufgabe 5 (Programmieren): Implementieren Sie alle der im Folgenden beschriebenen Funktionen in Haskell. Geben Sie jeweils auch die Typdeklarationen an. Verwenden Sie außer Listenkonstruktoren [] und : (und deren Kurzschreibweise) und Vergleichsoperatoren wie <=, ==,. . . keine vordefinierten Funktionen (dies schließt auch arithmetische Operatoren ein), außer denen, die in den jeweiligen Teilaufgaben explizit erlaubt werden. a) sekunden h m s Gibt die Anzahl der Sekunden zurück, die in h Stunden, m Minuten und s Sekunden vergehen. So berechnet zum Beispiel sekunden 5 4 3 den Wert 18243. Die Funktion darf sich auf negativen Eingaben beliebig verhalten. Sie dürfen hier + und * verwenden. b) turm x y Berechnet den Potenzturm x ↑↑ y. Der Potenzturm ist die logische Weiterentwicklung von Multiplikation und Potenzfunktion: Multiplikation a·n := a + a + ··· + a {z } | n Potenz n a := a | · a ·{z. . . · a} n a Potenzturm a ↑↑ n := a | · ·· (aa ) {z n ! } Beispiele: 2 2(2 ) • 2 ↑↑ 4 = 2 4 = 2(2 ) = 216 = 65.536 • 3 ↑↑ 3 = 7.625.597.484.987 Die Funktion darf sich auf negativen Eingaben beliebig verhalten. Sie dürfen auf - und ^ (zur Berechnung der Potenz) zurückgreifen. Hinweise: • Auch in Haskell gibt es unterschiedliche Integer-Datentypen mit verschiedenen Wertebereichen, weshalb in Ihrer Lösung vermutlich turm 3 3 nicht das erwartete Ergebnis 7625597484987 liefert. Wenn Sie den Datentyp Integer statt Int für die Darstellung ganzer Zahlen verwenden, vermeiden Sie das Problem. c) wurzel x Berechnet die abgerundete dritte (!) Wurzel aus x. Zum Beispiel liefert der Aufruf wurzel 124 den Wert 4. Die Funktion darf sich auf negativen Eingaben beliebig verhalten. Sie dürfen hier - und * verwenden. d) getEnd xs Berechnet das letzte Element der Int-Liste xs. Beispielsweise berechnet getEnd [12, 7, 23] den Wert 23. Die Funktion darf sich auf leeren Listen beliebig verhalten. e) insertEnd x ys Berechnet die Int-Liste, die entsteht, wenn man den Wert x an das Ende der Int-Liste ys einfügt. Beispielsweise berechnet insertEnd [12, 7] 23 die Liste [12, 7, 23]. f ) anzahl x ys Berechnet die Anzahl der Vorkommen von x in der Int-Liste ys. Zum Beispiel liefert der Aufruf anzahl 3 [1,2,3,4,5,4,3,2,1] den Wert 2. Sie dürfen hier + verwenden. g) einfuegen x ys Berechnet die Int-Liste, die entsteht, wenn man den Wert x so in die sortierte Int-Liste ys einfügt, dass diese anschließend weiterhin sortiert ist. Zum Beispiel liefert der Aufruf einfuegen 5 [1,3,5,7] den Wert [1,3,5,5,7]. 3 Programmierung WS12/13 Tutoriumslösung - Übung 10 (Abgabe 11.01.2013) h) insSort xs Sortiert die Int-Liste xs. Verwenden Sie dabei die Funktion einfuegen. Der Aufruf insSort [5,3,1,8] liefert dann beispielweise den Wert [1,3,5,8]. i) zipping xs ys Berechnet die Kombination der beiden Int-Listen xs und ys im Reißverschlussprinzip. Das heißt, dass das Ergebnis zuerst das erste Element von xs, dann das erste Element von ys, dann das zweite Element von xs usw. enthält. Wenn eine der beiden Listen leer ist, sollen nur noch Elemente aus der anderen Liste folgen. Beispielsweise berechnet zipping [1,2,5,8,7,6] [3,9,4] die Liste [1,3,2,9,5,4,8,7,6]. j) flach xs Berechnet für eine Liste von Int-Listen eine “flache” Liste, die die Elemente der inneren Listen der Eingabe xs in der ursprünglichen Reihenfolge enthält. Der Aufruf flach [[1,5,2],[],[2,3]] liefert zum Beispiel den Wert [1,5,2,2,3]. Lösung: -- a sekunden :: Int -> Int -> Int -> Int sekunden h m s = 60*60* h + 60* m + s -- b turm :: Integer -> Integer -> Integer turm _ 0 = 1 turm x y = x ^ ( turm x (y -1)) -- c wurzel :: Int -> Int wurzel 0 = 0 wurzel x = wurzelH x where wurzelH :: Int -> Int wurzelH w | w * w * w <= x = w | otherwise = wurzelH (w -1) -- d getEnd :: [ Int ] -> Int getEnd [ x ] = x getEnd ( _ : xs ) = getEnd xs -- e insertEnd :: Int -> [ Int ] -> [ Int ] insertEnd x [] = [ x ] insertEnd x ( y : ys ) = y : insertEnd x ys -- f anzahl :: Int -> [ Int ] -> Int anzahl _ [] = 0 anzahl x ( y : ys ) | x == y = 1 + anzahl x ys | otherwise = anzahl x ys -- g einfuegen :: Int -> [ Int ] -> [ Int ] einfuegen x [] = [x] einfuegen x ( y : ys ) | x < y = x : y : ys | otherwise = y : einfuegen x ys 4 Programmierung WS12/13 Tutoriumslösung - Übung 10 (Abgabe 11.01.2013) -- h insSort :: [ Int ] -> [ Int ] insSort [] = [] insSort ( x : xs ) = einfuegen x ( insSort xs ) -- i zipping :: [ Int ] -> [ Int ] -> [ Int ] zipping [] ys = ys zipping ( x : xs ) ys = x : zipping ys xs -- j flach flach flach flach :: [[ Int ]] -> [ Int ] [] = [] ( []: ys ) = flach ys (( x : xs ): ys ) = x : flach ( xs : ys ) 5