Programmierparadigmen Wintersemester 2011/12 Torsten Görg 7. Übung 8./9./10. Februar 2012 Organisatorische Hinweise Damit wir besser einschätzen können, wie aufwändig die Bearbeitung der Übungsaufgaben für Sie ist, und wieviel Zeit Sie dafür benötigen, möchten wir Sie bitten, in Ihrer Abgabe zu jeder Aufgabe die Zeit mit anzugeben, die Sie zur Bearbeitung dieser Aufgabe benötigt haben (inklusive Recherchen und Besprechungen im Abgabe-Team). Diese Zusatzangabe ist freiwillig und dient nur zur Verbesserung zukünftiger Aufgabengestaltungen. Aufgabe 7.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 Punkte In der Vorlesung wurde aufgezeigt, dass durch Wartungsänderungen an objektorientierten Programmen überraschende Probleme auftreten können. Dies soll nun an einem einfachen, in Java implementierten Beispielsystem betrachtet werden, das Sie in seiner Ausgangsform in der Datei Aufgabe_7_1.zip auf der Veranstaltungs-Homepage finden. Zu dem System gehören eine externe Bibliothek, die aus den beiden Klassen LibClassA und LibClassB besteht, und die eigentliche Applikation, die in der Klasse OwnClass realisiert ist. 1. Welche Ausgaben liefert dieses Beispielsystem? 2. Bei einem Bibliotheks-Update wird die Klasse LibClassA durch eine modifizierte Version ersetzt. Die modifizierte Klasse LibClassA ist in der Datei Aufgabe_7_1_Update.zip auf der VeranstaltungsHomepage bereitgestellt. Welche drei Fehler bzw. Probleme entstehen durch dieses Update im Zusammenspiel zwischen dem Bibliotheks-Code und dem Applikations-Code? Beachten Sie, dass die Bibliotheksentwickler nicht wissen, wie der Applikations-Code aussieht. Erläutern Sie jeweils, wie sich das Problem auf die Ausführung des Programms auswirkt. Gehen Sie davon aus, dass der gesamte Code in der Ausgangsform des Systems so beabsichtigt war. 3. Nachdem Sie im vorigen Aufgabenteil die Probleme identifiziert haben, die durch das BibliotheksUpdate entstanden sind, sollen Sie diese jetzt beheben. Obwohl die externe Bibliothek als Quelltext vorliegt, sind die Korrekturen ausschließlich im Applikations-Code vorzunehmen, damit auch künftig weiter Bibliotheks-Updates eingespielt werden können. Geben Sie eine korrigierte Version des Applikations-Codes an, in der die drei neu entstandenen Probleme so weit wie möglich behoben sind (Sie können dabei auch neue Klassen im Applikations-Code hinzufügen). 4. Wie hätte man durch geeignete Maßnahmen (sofern es solche gibt) im Applikations-Code, also in der Klasse OwnClass, vorab diese Probleme verhindern können oder zumindest erreichen können, dass diese frühzeitig (durch den Compiler) erkannt worden wären. Hinweis: Beachten Sie unter anderem die Möglichkeiten der Standard-Annotationen in Java. Aufgabe 7.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 Punkte In dieser und den nächsten beiden Aufgaben werden einige Konzepte der funktionalen Programmierung, die in der Vorlesung vorgestellt worden sind, an Hand von Beispielen in der rein-funktionalen Programmiersprache Haskell vertiefend betrachtet. Es werden keine Vorkenntnisse in Haskell oder einer anderen funktionalen Sprache vorausgesetzt. Alle Informationen, die Sie zur Bearbeitung der Aufgaben benötigen, finden Sie im Vorlesungsskript und in einem Haskell-Tutorial, das auf der Veranstaltungs-Homepage 1 als Material zu dieser Übung bereitgestellt ist (HaskellTutorial.pdf). Arbeiten Sie zunächst dieses Tutorial durch, bevor Sie die Beantwortung der folgenden Fragen angehen. Wenn Sie die Haskell-Beispiele ausprobieren möchten, installieren Sie am besten den Haskell-Interpreter Hugs. Auf http://www.haskell.org/hugs sind Installationspakete für diverse Plattformen verfügbar. 1. Geben Sie ein Haskell-Ausdruck an, der eine Liste mit den Integer-Elementen 13, 76, 5, 99 und 27 in dieser Reihenfolge erzeugt (zu Listen in Haskell siehe Abschnitt 2.2.5 im Tutorial). 2. Geben Sie ein Haskell-Ausdruck an, der ein 5-Tupel mit den Elementen ’p’, 5.87, "xyz", 84 und 1.3 in dieser Reihenfolge erzeugt (zu Tupeln in Haskell siehe Abschnitt 2.2.6 im Tutorial). 3. Geben Sie eine Haskell-Funktion createList_x an, die einen Integer-Wert als Parameter entgegennimmt und als Resultat eine Integer-Liste zurückliefert, die fünf mal genau diesen Integer-Wert enthält, so dass z.B. der Funktionsaufruf createList_x 7 die Liste [7, 7, 7, 7, 7] als Resultat liefert. Ihre Funktionsdefinition soll sowohl die Typdeklaration der Funktion als auch deren Implementierung umfassen. Haskell-Funktionen sind in den Abschnitten 1.3 und 2.3 im Tutorial beschrieben, diverse Beispiele zu Funktionen mit Listen als Rückgabewerten finden Sie in Abschnitt 3.2. 4. Schreiben Sie eine Haskell-Funktion createList_x_n, die eine Liste mit n Wiederholungen eines Integer-Werts x erzeugt und als Resultat zurückliefert. Diese Funktion soll zwei Integer-Parameter entgegennehmen, mit dem ersten Parameter wird der Wert für x übergeben, mit dem zweiten der Wert für n. Geben Sie auch die Typdeklaration der Funktion createList_x_n an. Hinweis: Verwenden Sie zum Konstruieren der Liste Rekursion (siehe Abschnitt 2.7 im Tutorial). 5. In Haskell kann eine Funktion partiell ausgewertet werden, indem sie nur mit einem Teil ihrer Parameter ”aufgerufen” wird. Dabei entsteht eine neue Funktion, deren Signatur nur noch die restlichen Parameter umfasst. Implementieren Sie eine Haskell-Funktion createList_37_n mit einem Integer-Parameter n, die eine Liste mit n Wiederholungen der Integer-Zahl 37 erzeugt und als Resultat zurückliefert. Definieren Sie dazu createList_37_n als partielle Auswertung der Funktion createList_x_n aus dem vorigen Aufgabenteil, wobei Sie nur einen aktuellen Parameter an createList_x_n übergeben (weitere Details zu partieller Auswertung finden Sie in Abschnitt 2.6 im Tutorial). Geben Sie auch die Typdeklaration der Funktion createList_37_n an. Aufgabe 7.3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 Punkte Gegeben sei folgenes Haskell-Programm: 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 fzip :: [Int] -> [Int] -> (Int -> Int -> Int) -> [Int] fzip [] [] _ = [] fzip (head1:tail1) (head2:tail2) f = (f head1 head2) : (fzip tail1 tail2 f) fold :: [Int] -> Int -> (Int -> Int -> Int) -> Int fold [] unit _ = unit fold (head:tail) unit f = f head (fold tail unit f) add :: Int -> Int -> Int add x y = x + y mult :: Int -> Int -> Int mult x y = x * y calc :: [Int] -> [Int] -> Int calc m n = fold (fzip m n mult) 0 add 1. Was berechnet und liefert dieses Haskell-Programm, wenn man es mit dem Funktionsaufruf calc [4, 3, 7] [9, 5, 2] startet? 2. Welche der Funktionen dieses Haskell-Programms sind Funktionen höherer Ordnung? 2 3. Beschreiben Sie, welche Funktionalität durch die Funktionen zip, fold, add, mult und calc jeweils realisiert ist, und erläutern Sie, wie diese Funktionen funktionieren. 4. Konvertieren Sie das gegebene Haskell-Programm in ein äquivalentes Java-Programm. a) Implementieren Sie dazu eine Java-Klasse Aufgabe_7_3, die die statischen Methoden zip, fold, add, mult und calc enthält. Diese Methoden sollen jeweils die gleiche Funktionalität realisieren wie die gleichnamigen Haskell-Funktionen. b) Setzen Sie Haskell-Listen mit der Java-Standardklasse Vector um, die bereits in den vorigen Übungen verwendet wurde. Aufgabe 7.4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 Punkte Eine quadratische Polynomfunktion lässt sich im Allgemeinen durch eine Funktion der Form f (x) = c1 · x2 + c2 · x + c3 beschreiben. Zeichnet man den Funktionsgraphen einer solchen Funktion, ergibt sich eine Parabel. 1. Implementieren Sie eine Haskell-Funktion f1, die entsprechend der obigen Definition den Funktionswert einer quadratischen Polynomfunktion mit den Koeffizienten c1, c2, und c3 an der Stelle x berechnet und zurückgibt. f1 soll zwei Parameter entgegennehmen, einen für die Koeffizienten c1, c2, und c3, wobei die Koeffizienten zu einem Tupel zusammengefasst sind, und einen Parameter für die Auswertungsstelle x. Geben Sie auch die Typdeklaration der Funktion f1 an (es soll mit dem Gleitkommazahlentyp Float gerechnet werden). 2. Wendet man vor der Auswertung der Funktion f(x) jeweils eine Modifikationsfunktion g auf x an ( f(g(x)) ), so kann man die zugehörige Parabel entlang der x-Achse verschieben, strecken und stauchen. Implementieren Sie eine Haskell-Funktion f2 als Erweiterung von f1, die als Funktion höherer Ordnung eine Funktion g, die von Float auf Float abbildet, als zusätzlichen Parameter entgegennimmt. Bei der Berechnung der Funktionswerte in f2 ist nun bei allen Vorkommen von x zunächst die Parameterfunktion g auf x anzuwenden. Auf diese Weise lassen sich beliebige Modifikationen an den x-Werten erreichen, indem man eine entsprechende Modifikationsfunktion g an f2 übergibt. Geben Sie auch die Typdeklaration der Funktion f2 an. Details zu Funktionen höherer Ordnung finden Sie im Abschnitt 3.2 im Tutorial. 3. Die Funktion f2 aus dem vorigen Aufgabenteil soll nun verwendet werden, um eine Parabel a) um shift Einheiten nach rechts zu verscheiben, indem x durch x - shift ersetzt wird, b) um den Faktor stretch gestaucht werden, indem x durch x * shift ersetzt wird (es soll vernachlässigt werden, dass es bei Parabeln, deren Scheitelpunkt nicht auf der y-Achse liegt, hierbei auch zu einer Verschiebung kommt). Definieren Sie für beide Modifikationen jeweils eine Haskell-Funktion g, die x in einen modifizierten Wert transformiert, und geben Sie jeweils ein Beispiel an, in dem f2 mit g als Parameterfunktion aufgerufen wird. 3