Programmierung WS12/13 Lösung - Übung 10 Prof.aa Dr. J. Giesl 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 ) absteigend berechnet die absteigende Liste der natürlichen Zahlen bis hinunter zu 1. Zum absteigend 5 die Liste [5,4,3,2,1]. Die Funktion produkt multipliziert die Elemente Liste, beispielsweise ergibt produkt [3,5,2,1] die Zahl 30. Die Funktion summe addiert die Elemente Liste. Zum Beispiel liefert summe [5,2,7] den Wert 14. Die Funktion Beispiel berechnet einer einer Geben Sie alle Zwischenschritte bei der Auswertung der folgenden Ausdrücke an: 1. produkt (absteigend 2) 2. summe (absteigend 2) Beachten Sie, dass wie Haskell eine Leftmost-Outermost Auswertungsstrategie besitzt. Allerdings sind Operatoren * 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 Lösung - Übung 10 → 2+1 →3 Aufgabe 2 (Auswertungsstrategie): Gegeben sei das folgende (7 Punkte) Haskell-Programm: produkt :: [ Int ] -> Int produkt [] = 1 produkt ( x : xs ) = x * produkt xs jedesZweite jedesZweite jedesZweite jedesZweite :: [ Int ] -> [ Int ] [] = [] [x] = [x] ( x : _ : xs ) = x : jedesZweite xs minus10 :: [ Int ] -> [ Int ] minus10 [] = [] minus10 ( x : xs ) = x - 10 : minus10 xs Die Funktion Zahl 30. produkt Die Funktion multipliziert die Elemente einer Liste, beispielsweise ergibt jedesZweite produkt [3,5,2,1] die bekommt eine Liste als Eingabe und gibt die gleiche Liste zurück, wobei jedes zweite Element gelöscht wurde. So ergibt jedesZweite [1,2,3] die Liste [1,3]. Die Funktion minus10 gibt seine Eingabeliste zurück, wobei bei von jedem Element 10 subtrahiert wurde. Geben Sie alle Zwischenschritte des Ausdrucks produkt (jedesZweite (minus10 [3,2,1])) bei der Ausp, j und m statt produkt, jedesZweite und minus10. wertung an. Schreiben Sie hierbei um Platz zu sparen Hinweise: Beachten Sie, dass ratoren wie * und Haskell eine Leftmost-Outermost Auswertungsstrategie besitzt. Allerdings sind Ope- +, die auf eingebauten Zahlen arbeiten, strikt, d.h. hier müssen vor Anwendung des Operators seine Argumente vollständig ausgewertet worden sein (wobei zunächst das linke Argument ausgewertet wird). Lösung: p (j (m [3,2,1])) → p (j (3 - 10 : m [2,1])) → p (j (3 - 10 : 2 - 10 : m [1])) → p (3 - 10 : j (m [1])) → (3 - 10) * p (j (m [1])) → (-7) * p (j (m [1])) → (-7) * p (j (1 - 10 : m [])) → (-7) * p (j (1 - 10 : [])) → (-7) * p (1 - 10 : []) → (-7) * ((1 - 10) * p []) → (-7) * ((-9) * p []) → (-7) * ((-9) * 1) → (-7) * (-9) → 63 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. 2 Programmierung WS12/13 Lösung - Übung 10 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 entsteht, wenn die Elemente aus ys in der Reihenfolge wie sie in ys xs ++ ys ist die Liste, die 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 b) Der erste Ausdruck ist nicht typkorrekt, da im Teilausdruck n+1 x:y [Int]. und vom Typ 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 vom Typ d) [Int]. n+2 und ist Die Ausdrücke sind also nicht gleich. Beides sind typkorrekte Listenausdrücke (äquivalent zu vom Typ e) [Int]. [x,y,x,x] ++ xs der Länge n + 4 und ebenfalls Die Ausdrücke sind also gleich. [[],[],[[1]]], also eine dreielementige Liste, die [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, Der linke Ausdruck ergibt ausgeschrieben die Liste Listen von Listen enthält. Der genaue Typ der innersten Liste ist aber nicht die gleichen Listen darstellen, sind die Ausdrücke also nicht gleich. Aufgabe 4 (Listen): Seien x, y, z ganze Zahlen vom Typ (9 Punkte) 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: Falls linke und rechte Seite gleich sind, genügt wiederum a) [] ++ [xs] = [] : [xs] 3 eine Angabe des Typs und der Elementzahl Programmierung WS12/13 Lösung - Übung 10 b) [[]] ++ [x] = [] : [x] c) [x] ++ [y] = x : [y] d) x:y:z:(xs ++ ys) = [x,y,z] ++ xs ++ ys e) [(x:xs):[ys],[[]]] = (([]:[]):[]) ++ ([([x] ++ xs),ys]:[]) Lösung: a) Beide Ausdrücke haben den Typ [[Int]]. Jedoch hat die erste Liste ein Element, während die zweite Liste zwei Elemente besitzt. Die Ausdrücke sind also nicht gleich. b) Beide Ausdrücke sind nicht typkorrekt. Daher würde die Gleichung in Haskell nicht gelten. Allerdings [[],x] würden beide Ausdrücke die gleiche (ungültige) Liste darstellen, nämlich (somit sind beide Ant- worten, ob die Gleichung gilt oder nicht, zulässig). Diese Liste ist nicht typkorrekt, da sie sowohl eine Int Liste, als auch einen c) Wert enthält. Die Gleichung gilt, denn beide Ausdrücke repräsentieren die Liste [x,y] vom Typ [Int], welche zwei Elemente enthält. x, y, z, anschlieÿend die n Elemente der Liste xs und schlieÿlich die m Elemente der Liste ys enthält. Diese Liste enthält also insgesamt 3 + n + m Elemente und ist vom Typ [Int]. d) Die Gleichung gilt, denn beide Ausdrücke repräsentieren die gleiche Liste, welche zuerst die Elemente e) Beide Ausdrücke sind vom gleichen Typ [[[Int]]] und sind Listen mit zwei Elementen. Diese Elemente [[]] und andererseits die Liste [x:xs,ys]), allerdings ist ihre sind auch noch gleich (einerseits die Liste Reihenfolge in den beiden Ausdrücken unterschiedlich, sodass die Gleichung nicht gilt. Tutoraufgabe 5 (Programmieren): Implementieren Sie alle der im Folgenden beschriebenen Funktionen in Typdeklarationen an. Verwenden Sie auÿer Listenkonstruktoren Vergleichsoperatoren wie <=, ==,. . . keine [] und Haskell. : Geben Sie jeweils auch die (und deren Kurzschreibweise) und vordenierten Funktionen (dies schlieÿt auch arithmetische Opera- toren ein), auÿer denen, die in den jeweiligen Teilaufgaben explizit erlaubt werden. a) sekunden h m s h Stunden, m Minuten und s Sekunden vergehen. So sekunden 5 4 3 den Wert 18243. Die Funktion darf sich auf negativen Eingaben dürfen hier + und * verwenden. Gibt die Anzahl der Sekunden zurück, die in berechnet zum Beispiel beliebig verhalten. Sie b) turm x y x ↑↑ y . Berechnet den Potenzturm Der Potenzturm ist die logische Weiterentwicklung von Multiplikation und Potenzfunktion: Multiplikation a·n := a + a + ··· + a {z } | an := a | · a ·{z. . . · a} n Potenz n a· a ↑↑ n Potenzturm Beispiele: 2 2(2 ) 2 ↑↑ 4 = 2 4 = 2(2 ) = 216 = 65.536 4 := a | (aa ) ·· {z n ! } Programmierung WS12/13 Lösung - Übung 10 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, wes- turm 3 3 nicht das erwartete Ergebnis 7625597484987 liefert. Integer statt Int für die Darstellung ganzer Zahlen verwenden, vermeiden halb in Ihrer Lösung vermutlich Wenn Sie den Datentyp Sie das Problem. c) wurzel x Berechnet die abgerundete dritte (!) Wurzel aus 4. x. Zum Beispiel liefert der Aufruf wurzel 124 den Wert - und * verwenden. Die Funktion darf sich auf negativen Eingaben beliebig verhalten. Sie dürfen hier d) getEnd xs Berechnet das letzte Element der 23. Int-Liste xs. Beispielsweise berechnet getEnd [12, 7, 23] den Wert Die Funktion darf sich auf leeren Listen beliebig verhalten. e) insertEnd x ys Berechnet die Int-Liste, Beispielsweise berechnet f ) anzahl x ys Berechnet die Anzahl der Vorkommen von 3 [1,2,3,4,5,4,3,2,1] x an das Ende [12, 7, 23]. die entsteht, wenn man den Wert insertEnd [12, 7] 23 den Wert 2. die Liste der Int-Liste ys einfügt. x in der Int-Liste ys. Zum Beispiel liefert der Aufruf anzahl + verwenden. Sie dürfen hier g) einfuegen x ys Int-Liste, die entsteht, wenn man den Wert x so in die sortierte Int-Liste ys einfügt, dass einfuegen 5 [1,3,5,7] den [1,3,5,5,7]. Berechnet die diese anschlieÿend weiterhin sortiert ist. Zum Beispiel liefert der Aufruf Wert h) insSort xs Sortiert die Int-Liste xs. Verwenden Sie dabei die Funktion einfuegen. Der Aufruf insSort [5,3,1,8] [1,3,5,8]. liefert dann beispielweise den Wert i) zipping xs ys Berechnet die Kombination der beiden Int-Listen xs und ys im Reiÿverschlussprinzip. Das heiÿt, dass xs, dann das erste Element von ys, dann das zweite Element das Ergebnis zuerst das erste Element von von xs usw. enthält. Wenn eine der beiden Listen leer ist, sollen nur noch Elemente aus der anderen Liste zipping [1,2,5,8,7,6] [3,9,4] die Liste [1,3,2,9,5,4,8,7,6]. folgen. Beispielsweise berechnet j) flach xs Berechnet für eine Liste von Eingabe xs Int-Listen eine ache Liste, die die Elemente der inneren Listen der in der ursprünglichen Reihenfolge enthält. Der Aufruf 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)) 5 flach [[1,5,2],[],[2,3]] liefert Programmierung WS12/13 Lösung - Übung 10 -- 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 -- 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 ) Aufgabe 6 (Programmieren): (1+1+2+1+1+1+2+2+2+2 = 15 Punkte) Implementieren Sie alle der im Folgenden beschriebenen Funktionen in Typdeklarationen an. Verwenden Sie auÿer Listenkonstruktoren Vergleichsoperatoren wie <=, ==,. . . keine [] und Haskell. : Geben Sie jeweils auch die (und deren Kurzschreibweise) und vordenierten Funktionen (dies schlieÿt auch arithmetische Opera- toren ein), auÿer denen, die in den jeweiligen Teilaufgaben explizit erlaubt werden. a) millimeter f z Gibt (halbwegs präzise) aus, wie viele Millimeter 6 f Fuÿ und z Zoll sind. Gehen Sie hierbei davon aus, Programmierung WS12/13 Lösung - Übung 10 305mm entspricht und ein Zoll genau 25mm ist. So berechnet 1625. Die Funktion darf sich auf negativen Eingaben beliebig dass ein Fuÿ 4 den Wert und * zum Beispiel millimeter 5 + verhalten. Sie dürfen hier verwenden. b) mult x y Berechnet x · y. Sie dürfen dazu + und - verwenden. Die Funktion darf sich auf negativen Eingaben beliebig verhalten. c) bLog x x. Somit liefert bLog 1 den Wert 0, bLog 5 5. Sie dürfen hier + und * verwenden. Die Funktion darf sich auf Eingabe 0 beliebig verhalten. Berechnet den aufgerundeten Logarithmus zur Basis 2 von den Wert 3 und bLog 32 den Wert negativen Eingaben oder der d) getLastTwo xs Int-Liste xs. Beispielsweise berechnet getLastTwo [12, 7, 23] den Wert [7, 23]. Die Funktion darf sich auf Listen der Länge 0 und 1 beliebig verhalten. Berechnet die Teilliste der letzten zwei Elemente der e) singletons x x Berechnet eine Liste mit Elementen, wobei jedes Listenelement eine einelementige Liste ist. Die Ele- mente der einelementigen Listen sind die Zahlen die Liste hier - [[3],[2],[1]]. x, x − 1, . . . , 1. Beispielsweise berechnet singletons 3 Die Funktion darf sich auf negativen Eingaben beliebig verhalten. Sie dürfen verwenden. f ) cleanEmpty xs Die Eingabe ist eine Liste von Listen von Zahlen (vom Typ [[Int]]). Die Funktion berechnet die Liste von Listen von Zahlen, die aus der Eingabe entsteht, indem alle leeren Listen aus dieser entfernt werden. Beispielsweise berechnet cleanEmpty [[5,6],[],[8],[]] g) kleinstes xs Berechnet das kleinste Element, das in der kleinstes [42, 7, 23] den Wert 7. die Liste Int-Liste xs [[5,6],[8]]. vorkommt. Zum Beispiel liefert der Aufruf Die Funktion darf sich auf leeren Listen beliebig verhalten. h) nachHinten x xs Verschiebt die x ersten Elemente der Liste xs an das Ende. Beispielsweise berechnet nachHinten 2 [1,2,3,4] die Liste [3,4,1,2]. Sie dürfen hier ++ und - verwenden. Die Funktion darf sich auf negativen Eingaben für x oder bei einer leeren Eingabeliste beliebig verhalten. i) einpacken xs ys Das erste Argument xs ist eine Liste von Listen von Zahlen (vom Typ ist eine einfache Liste von Zahlen (vom Typ [Int]). [[Int]]), das zweite Argument Die Funktion ersetzt leere Listen in der ersten Eingabeliste durch einelementige Listen. Der Inhalt dieser einelementigen Listen ist die jeweils nächste Zahl aus der zweiten Eingabeliste. Beispielsweise berechnet Liste [[5,6],[1],[8],[2]]. einpacken [[5,6],[],[8],[]] [1,2] die Wenn im zweiten Argument nicht genug Zahlen zur Verfügung stehen, werden zusätzliche leere Listen im ersten Argument leer gelassen. Wenn im zweiten Argument zu viele Zahlen zur Verfügung stehen, werden diese ignoriert. j) listAdd x xs Addiert jeweils das x n-te Listenelement auf das (n+1)-te Listenelement, wobei auf das erste Listenelement listAdd 5 [1,9,3] die Liste [6,10,12]. Sie dürfen hier + addiert wird. Beispielsweise berechnet verwenden. Lösung: -- a millimeter :: Int -> Int -> Int millimeter feet inch = 305* feet + 25* inch 7 Programmierung WS12/13 Lösung - Übung 10 -- b mult :: Int -> Int -> Int mult _ 0 = 0 mult x y = x + mult x (y -1) -- c bLog :: Int -> Int bLog 1 = 0 bLog x = bLogH 2 1 where bLogH :: Int -> Int -> Int bLogH y r | x > y = bLogH ( y *2) ( r +1) | otherwise = r -- d getLastTwo :: [ Int ] -> [ Int ] getLastTwo [x , y ] = [x , y ] getLastTwo ( _ : x : xs ) = getLastTwo ( x : xs ) -- e singletons :: Int -> [[ Int ]] singletons 0 = [] singletons n = [ n ] : singletons (n -1) -- f cleanEmpty cleanEmpty cleanEmpty cleanEmpty :: [[ Int ]] -> [[ Int ]] [] = [] ([]: xs ) = cleanEmpty xs ( x : xs ) = x : cleanEmpty xs -- g kleinstes :: [ Int ] -> Int kleinstes ( x : xs ) = kleinstesH x xs where kleinstesH :: Int -> [ Int ] -> Int kleinstesH m [] = m kleinstesH m ( y : ys ) | m < y = kleinstesH m ys | otherwise = kleinstesH y ys -- alternative Loesung : kleinstes ' :: [ Int ] -> Int kleinstes ' [ x ] = x kleinstes ' ( x : y : xs ) | x < y = kleinstes ' ( x : xs ) | otherwise = kleinstes ' ( y : xs ) -- h nachHinten :: Int -> [ Int ] -> [ Int ] nachHinten _ [] = [] nachHinten n xs = nachHintenHelp [] n xs where nachHintenHelp :: [ Int ] -> Int -> [ Int ] -> [ Int ] nachHintenHelp xs 0 ys = ys ++ xs nachHintenHelp xs n ( y : ys ) = nachHintenHelp ( xs ++ [ y ]) (n -1) ys -- alternative Loesung : nachHinten ' :: Int -> [ Int ] -> [ Int ] nachHinten ' _ [] = [] 8 Programmierung WS12/13 Lösung - Übung 10 nachHinten ' 0 xs = xs nachHinten ' n ( x : xs ) = nachHinten ' (n -1) ( xs ++[ x ]) -- i einpacken einpacken einpacken einpacken einpacken :: [[ Int ]] -> [ Int ] -> [[ Int ]] [] _ = [] xs [] = xs ([]: xs ) ( y : ys ) = [ y ] : einpacken xs ys ( x : xs ) ( y : ys ) = x : einpacken xs ( y : ys ) -- j listAdd :: Int -> [ Int ] -> [ Int ] listAdd _ [] = [] listAdd z ( x : xs ) = ( z + x ) : listAdd x xs 9