Lehrstuhl für Softwaretechnik und Programmiersprachen Jens Bendisposto Funktionale Programmierung – WS 13/14 Zusatzblatt zur Klausurvorbereitung Achtung: Dieses Blatt ist keine Probeklausur. Es ist keinesfalls eine angemessene Vorbereitung nur die Aufgaben dieses Blattes zu lösen. Die Aufgaben sind in zufälliger Reihenfolge. Die Musterlösung werde ich am kommenden Freitag (14.2.2012) veröffentlichen. Aufgabe 9.1 (Punktevergabe) In einer Klausur soll eine Multiple Choice Aufgabe bewertet werden. Zu jeder Frage gibt es zwei Antwortmöglichkeiten. Für das Bewertungsschema sollen folgende Eigenschaften gelten: 1. Ein Student der ausschliesslich rät, soll im Mittel 0 Punkte bekommen 2. Es sollen keine negativen Punkte vergeben werden 3. Maximal sollen k Punkte vergeben werden Sei k die Anzahl der maximalen Punkte und x die Anzahl der richtig angekreuzten Lösungen Folgendes Schema erfüllt die Bedingungen: ( 0 x ≤ k2 p= 2(x − k2 ) sonst a) Implementieren Sie eine Funktion (defn punkte [x k] ...), die die Punkte ausrechnet. b) Überprüfen Sie, dass die Eigenschaften gelten. c) Angenommen ein Student kennt die Antwort auf eine Frage nicht. Kann sich der Student durch Raten verschlechtern? Aufgabe 9.2 (Datenstrukturen) Die Banker’s Queue ist eine funktionale Datenstruktur, die in der Praxis gut funktioniert. Die Datenstruktur besteht aus zwei Listen. Aus einer Liste (front) werden Elemente herausgeholt, in die andere (rear) werden Elemente abgelegt. Wenn die rear Liste länger ist als die Front-Liste wird die rear Liste umgedreht, an die front Liste angehangen und geleert. a) Implementieren Sie die Banker’s Queue mit Hilfe eines Records und eines Protocols. Das Protokoll soll 2 Funktionen push und take spezifizieren. b) push bekommt als Parameter die Queue und ein Element, das eingefügt wird. Zurückgegeben wird eine Queue c) take bekommt als Parameter die Queue. Zurückgegeben wird eine Queue, das entfernte Element wird geprintet. d) In welcher Reihenfolge müssen die Argumente von push sein? Warum? Was könnte man tun, wenn man die Reihenfolge umdrehen will? Aufgabe 9.3 (Macros und Funktionen) Gegeben seine folgende Implementierungen des logischen Operators or. Geben Sie einen Aufuf an, bei dem sich das Verhalten der beiden Implementierung voneinander unterscheidet und erklären Sie, warum sich die Implementierungen unterscheiden. (defmacro or-m ([] nil) ([x] x) ([x & more] `(let [or# ~x] (if or# or# (or-m ~@more))))) (defn or-fn ([] nil) ([x] x) ([x & more] (if x x (apply or-fn more)))) Aufgabe 9.4 (Lazyness) Angenommen Clojure hätte keine Funktion iterate. Implementieren Sie eine Funktion, die eine pure Funktion f und einen Startwert x bekommt und eine unendliche Sequenz mit den Werten x, (f x), (f (f x)), (f (f (f x))), ... zurückgibt. Aufgabe 9.5 (Higher Order Funktionen) Beim Kartenspiel Black-Jack ist es das Ziel eines Spielers mit seinen Karten möglichst nahe an den Wert 21 zu kommen. Asse werden als 1 oder 11 gezählt. Andere Bildkarten haben eine Wert von 10. Die Karten 2-10 haben den aufgedruckten Wert. Ein Spieler, der einen Wert erzielt, der grösser ist als 21 hat automatisch verloren. Die niedrigen Karten 2-10 werden durch die entsprechenden nummerischen Werte repräsentiert. Bildkarten werden durch :bube, :dame, :koenig aund :ass repräsentiert. a) Implementieren Sie eine Clojure Funktion, die aus einer gegebenen Kartensequenz die optimale Kartensumme berechnet. => (opt-val [2 5 7]) 14 => (opt-val [10 :ass]) 21 => (opt-val [10 9 :ass]) 20 b) Schreiben Sie eine Funktion, die eine Sequenz von Kartensequenzen bekommt und den oder die Sieger berechnet. Ausgabe ist eine Sequenz der Siegersequenzen. Aufgabe 9.6 (Fixpunkt) Die Eulersche Zahl e kann mit Hilfe folgender Summe ausgerechnet werden. e= ∞ X 1 1 1 1 =1+1+ + + + ··· k! 2 6 24 k=0 Schreiben Sie eine Clojure Funktion, die e mit einer Genauigkeit von 10−3 berechnet. Geben Sie alle benötigten Hilfsfunktionen an. Aufgabe 9.7 (Multimethoden) Aus einer Legacy Anwendung sollen Daten importiert werden. Es handelt sich dabei um Koordinaten. Das Problem ist, das zwei und dreidimensionale Koordinaten gemischt in einem Bytestrom gesendet werden. Unsere Anwendung erhält die Daten als einen einzigen String. Die einzelnen Zahlen sind durch Leerzeichen voneinander getrennt, die Koordinaten durch Zeilenumbrüche. Ein Beispiel-Datenstrom könnte so aussehen: (def input "23 8 15\n11 0\n0 5\n-10 15 9") user=> (map identity input) (\2 \3 \space \8 \space \1 \5 \newline \1 \1 \space \0 \newline \0 \space \5 \newline \- \1 \0 \space \1 \5 \space \9) a) Implementieren Sie eine Funktion, die aus dem String eine Sequenz von Sequenzen generiert, in der die Koordinaten voneinander getrennt sind. Im Beispiel sollte es so funktionieren. Sie können davon ausgehen, dass Sie read-string sicher verwenden können. user=> (as-seq input) ([23 8 15] [11 0] [0 5] [-10 15 9]) ;; Auch ok: ;; ((23 8 15) (11 0) (0 5) (-10 15 9)) ;; [[23 8 15] [11 0] [0 5] [-10 15 9]] b) In Clojure wollen wir die Daten in Records speichern. Dazu sind folgende beiden Vektoren definiert: (defrecord Vec2d [x y]) (defrecord Vec3d [x y z]) Implementieren Sie eine Multimethod, die folgendermassen funktioniert: user=> (map as-vector (as-seq input)) (#user.Vec3d{:x 23, :y 8, :z 15} #user.Vec2d{:x 11, :y 0} #user.Vec2d{:x 0, :y 5} #user.Vec3d{:x -10, :y 15, :z 9}) c) Kann man das Problem auch mit Protokollen sinnvoll lösen? Falls ja: Geben Sie eine Implementierung an. Falls nein: Erklären Sie warum es nicht gut geht. Aufgabe 9.8 (Monaden) Beim Fizzbuzz Problem sollen in einer Sequenz von Zahlen alle durch 3 teilbaren Zahlen durch :fizz, alle durch 5 teilbaren Zahlen durch :buzz und alle durch 3 und 5 teilbaren Zahlen durch :fizzbuzz ersetzt werden. Im Folgenden werden wir eine Variante des Problems betrachten. Statt :fizzbuzz soll nichtdeterministisch entweder :fizz oder :buzz eingesetzt werden. a) Schreiben Sie eine Funktion fizzbuzz-mfn für die Sequenz-Monade, die für eine gegebene Zahl die nichtdeterministische Berechnung durchführt. b) Schreiben Sie eine Funktion fizzbuzz-m, die eine Sequenz von Zahlen bekommt und die nichtdeterministische Funktion für die Sequenz durchführt. Ergebnis sollen alle möglichen Sequenzen sein, die beim Ausführen der Berechnung auftreten können. Hinweis: Die monadische map Funktion m-map könnte helfen. Beispielaufruf: => (fizzbuzz-m [1 2 ((1 2 :fizz 4 :buzz (1 2 :fizz 4 :buzz (1 2 :fizz 4 :buzz (1 2 :fizz 4 :buzz 3 7 7 7 7 4 5 7 :fizz :fizz :buzz :buzz 15 16 16 16 16 16 15]) :fizz) :buzz) :fizz) :buzz)) Aufgabe 9.9 (Monaden) a) Gegeben sei eine Funktion f mit dem Typ a->b. Welchen Typ hat die Funktion, wenn man m-lift in der Sequenzmonade auf die Funktion anwendet? b) Ist die geliftete Funktion eine monadische Funktion? Was ist eine monadische Funktion? Aufgabe 9.10 (Haskell) Gegeben sei folgendes Haskell Programm. Übersetzen Sie es in Clojure. abc abc abc abc 0 n = (m+1) (m+1) _ _ = n+1 0 = abc m 1 (n+1) = abc m (abc (m+1) n) 0 Hinweis: Das Programm produziert sehr schnell sehr grosse Berechnungen, die StackOverflows erzeugen. Für n ≤ 3 müsste es aber gut funktionieren. Bei n = 3 darf m ebenfalls nicht gross sein. etwa m < 10. Aufgabe 9.11 (Problem) Gegeben sei folgende Implementierung der Fakultätsfunktion. Die Implementierung hat ein Problem. Was ist das Problem und wie kann man es lösen? Sie können davon ausgehen, dass die Funktion immer korrekt aufgerufen wird. (defn ! [n] (if (zero? n) 1 (*' n (! (dec n))))