Klausur - iLearn - Christian-Albrechts

Werbung
Christian-Albrechts-Universität zu Kiel
Institut für Informatik
Lehrstuhl für Programmiersprachen und Übersetzerkonstruktion
Prof. Dr. Michael Hanus, Sandra Dylus, Björn Peemöller
6. Klausur zur Vorlesung „Fortgeschrittene Programmierung“
SS 15
Hinweis: Sie können sich hier ein paar Statisken zur Klausur ansehen.
Diese Klausur besteht aus 6 Aufgaben auf 2 Seiten.
Gesamtpunktzahl: 65 Punkte, Punktzahl zum Bestehen: 30 Punkte.
Einlesezeit: 15 Minuten, Bearbeitungszeit: 120 Minuten.
Bitte schreiben Sie auf jedes Blatt, das Sie abgeben, Ihren Namen und Ihre Matrikelnummer!
Alle Aufgaben können unabhängig von einander bearbeitet werden, insbesondere auch einzelne Aufgabenteile.
Hierbei können Definitionen aus vorherigen Aufgabenteilen verwendet werden, auch wenn diese nicht gelöst
wurden.
Aus der Vorlesung bekannte Konstrukte (Prelude von Haskell, Prolog-Prädikate) dürfen verwendet werden.
Hinweis: Die Einsichtnahme in die korrigierten Klausuren findet am Dienstag, den 03.11., von 10:00 bis 11:00
Uhr im CAP4 (Hochhaus), Raum 715 statt. Bitte bringen Sie dazu einen Lichtbildausweis mit.
Viel Erfolg!
Aufgabe 1 - Niemand mag: [ ] Java; [ ] Prolog; [ ] Haskell; [ ] Klausuren schreiben
15 Punkte
Es könnte/n alle, mehrere, eine oder gar keine Antwort richtig sein. Schreiben Sie die Ihrer Meinung nach
richtigen Antworten zu jeder Frage auf; sollte keine der Antwortmöglichkeiten zutreffen, formulieren Sie dies
bitte explizit. Markierungen auf dem Aufgabenzettel werden nicht berücksichtigt!
Eine Abgabe könnte also beispielhaft wie folgt aussehen:
21.
22.
23.
24.
25.
a,b,c
keine Antwortmöglichkeit ist korrekt
a
d, e
a,b,c,d,e
Richtig zugeordnete Antworten ergeben Pluspunkte, falsch zugeordnete Antworten Minuspunkte; jede Frage
kann dabei mit minimal 0 Punkten und maximal 5 Punkten bewertet werden. Die Gesamtpunktzahl wird am
Ende durch 5 geteilt und abgerundet.
Java
1. Welche der folgenden Programm-Fragemente kompilieren?
a) LinkedList<?> list1 = null;
LinkedList<Integer super ?> list2 = null;
list2 = list1;
1
b) LinkedList<?> list1 = null;
LinkedList<? extends ?> list2 = null;
list2 = list1;
c) LinkedList<?> list1 = null;
LinkedList<? extends Integer> list2 = null;
list2 = list1;
d) LinkedList<?> list1 = null;
LinkedList<? super Integer> list2 = null;
list2 = list1;
e) LinkedList<?> list1 = null;
LinkedList<? super Integer> list2 = null;
list1 = list2;
2. Welche der folgenden Generic-Konstrukte beschreiben Bounded Wildcards?
a)
b)
c)
d)
e)
<?
<C
<?
<?
<_
super B>
super ?>
extends A>
implements C>
super C>
3. Welcher Thread wacht beim Aufruf von o.notify() auf?
a)
b)
c)
d)
e)
Der zuletzt schlafengelegte Thread von Objekt o wacht auf.
Mittels der Übergabe der Thread-ID kann ein bestimmter Thread geweckt werden.
Es wachen alle Threads von Objekt o auf und bewerben sich neu um das Lock.
Der zuerst schlafengelegte Thread von Objekt o wacht auf.
Es wird ein beliebiger der systemweit schlafenden Threads geweckt.
4. Wann wird ein Lock auf ein Objekt wieder freigegeben?
a)
b)
c)
d)
e)
Beim
Beim
Beim
Beim
Beim
Verlassen der synchronisierten Methode
Aufruf der Methode wait() auf ein anderes Objekt
Aufruf der Methode notify()
Aufruf der Methode wait()
Aufruf der Methode waitAll()
5. Welche der Aussagen gelten für Java Generics?
a)
b)
c)
d)
e)
Java Generics bezeichnet die Umsetzung von parametrischem Polymorphismus.
Es sind explizite Typecasts erforderlich.
Typfehlermeldungen könnten bereits zur Compile-Zeit ausgegeben werden.
Der Diamantoperator <> kann dabei immer verwendet werden, um den richtigen Typ zu inferieren.
Es ist keine Typsicherheit gegeben.
Prolog
6. Gegeben sei das folgende Prolog-Programm.
swap([A,B|L], [B,A|L]) :- B < A.
swap([A|L], [A|N]) :- swap(L, N).
Was ist das Ergebnis der Anfrage ?- swap([1,8,1,1],X).?
2
a)
b)
c)
d)
e)
X = [8,1,1,1]
X = [1,8,1,1]
false
X = [1,1,8,1]
X = [1,1,1,8]
7. Welche der Zeichenfolgen sind valide Listen in Prolog?
a)
b)
c)
d)
e)
[[4] | [5]]
[42, prolog | []]
[3 | 17]
[11,12]
[1,2,[3]]
8. Welche der Anfragen werden mit true beantwortet?
a)
b)
c)
d)
e)
?????-
121 is 11 * 11.
8 * 7 is 56.
3 * 7.
12 - 8 is 2 + 2.
4 + 5 is 4 + 5.
9. Vervollständigen Sie: σ heißt allgemeinster Unifikator, falls für alle Unifikatoren σ 0 eine Substitution φ
existiert mit
a)
b)
c)
d)
e)
φ = σ ◦ σ0
σ = σ0 ◦ φ
σ0 = σ ◦ φ
σ0 = φ ◦ σ
σ = φ + σ0
10. Welche der Aussagen über Prolog sind richtig?
a)
b)
c)
d)
e)
Die Auswertungsstrategie basiert auf Tiefensuche.
Prolog ist eine Untermenge der Prädikatenlogik 1. Stufe.
Das Komma (,) entspricht dem logischen Und (∧) und das Semikolon (;) dem logischen Oder (∨).
Atome enthalten nur Kleinbuchstaben.
Die Implementierung von Relationen ist in Prolog in Form von Funktionen möglich.
Haskell
11. Der Ausdruck [(1,True),(0,False)] kann wie folgt ohne weitere Hilfsdefinitionen getypt werden:
a)
b)
c)
d)
e)
[(Int,Bool)]
[Pairs]
Ord a => [(a,Bool)]
[Int,Bool]
[(Int,Bool),(Int,Bool)]
12. Die Berechung des Ausdrucks [(y,x) | x <- [1..3], y <- [1..2]] ergibt:
a)
b)
c)
d)
[(1,1),(1,2),(1,3),(2,1),(2,2),(2,3)]
[(1,1),(2,2),(1,3),(2,1),(1,2),(2,3)]
[(1,2),(2,1),(3,2),(1,1),(2,2),(3,1)]
[(1,1),(1,2),(2,1),(2,2),(3,1),(3,2)]
3
e) [(1,1),(2,1),(3,1),(1,2),(2,2),(3,2)]
13. Die Funktion nonsense x y = x y y kann wie folgt getypt werden:
a)
b)
c)
d)
e)
a -> a -> a
(b -> b -> a)
(a -> b -> b)
(a -> a -> a)
(a -> a -> b)
->
->
->
->
b
b
a
a
->
->
->
->
a
a
a
b
14. Der Ausdruck Branch (Tip 1) (Tip 2) ist ein Wert der folgenden Datentypen:
a)
b)
c)
d)
e)
data
data
data
data
data
Tree
Tree
Tree
Tree
Tree
a
a
a
a
a
=
=
=
=
=
Tip
Tip
Tip
Tip
Tip
a | Branch a a
a | Branch (Tree a) (Tree a)
(Tree a) | Branch Int Int
(Tree a) | Branch (Tree a) (Tree a)
Int | Branch (Tree a) (Tree a)
15. Welche der folgenden Listen sind in Haskell nicht valide (ergeben einen Typfehler)?
a)
b)
c)
d)
e)
[[],[]]
[(),(1),(2)]
[[1],[False]]
[False, [True]]
[[1,2],[]]
Aufgabe 2 - Hobby: Constraint- und Metaprogrammierung
10 Punkte
Abbildung 1: Quelle - http://xkcd.com/287/
1. Modellieren Sie das in Abbildung 1 dargestellte Problem mit Hilfe von Prolog und Finite Domain
Constraints. Definieren Sie also ein Prädikat xkcd, das alle möglichen Vorspeisekombinationen berechnet,
die in der Summe ihrer Preise $15.05 ergeben.
Dabei soll der Aufruf
? - xkcd([Fruits,Fries,Salad,Wings,Sticks,Plate]).
die Anzahl der Portionen der jeweiligen Vorspeise ausgeben.
2. In der Vorlesung haben Sie folgenden Meta-Interpretierer für Prolog betrachtet:
prove(true) :- !.
% Rumpf von Fakten
prove( (A,B) ) :- !,
prove(A),
% Beweise 1. Teilziel
prove(B).
% Beweise 2. Teilziel
prove(A) :clause(A,G),
% Suche passende Klausel
prove(G).
% Beweise Klauselrumpf
4
Erweitern Sie diesen Meta-Interpretierer so, dass dieser bei einer erfolgreichen Ableitung die Liste aller
der dabei bewiesenen Literale zurückgibt. So soll z.B. für das Programm
p(X) :- q(X).
q(a).
die Erweiterung zu proveList die folgenden Ergebnisse liefern.
?- proveList(p(X),Ls).
Ls = [p(a),q(a)]
X = a.
Hinweis: Mit dem Prädikat clause(H,B) kann auf die vorhandenen Klauseln zugegriffen werden. Dieses
Prädikat ist beweisbar, falls die Klausel H :- B. existiert, wobei H keine Variable sein darf.
10 Punkte
Aufgabe 3 - Refactoring
1. In dieser Teilaufgabe sollen Sie die folgenden “Meisterwerke”, die von Ihren Kommilitonen (so oder so
ähnlich) als erste Haskell-Programme abgegeben wurden, mit Hilfe von Pattern Matching und “scharfem
Hinsehen” vereinfachen. Die vereinfachte Version soll dabei weder if-then-else-Sequenzen noch Guards
verwenden.
ggT :: Integer -> Integer -> Integer
ggT a b = if b==0
then a
else ggT b (mod a b)
isPrefixOf :: Eq a => [a] -> [a] -> Bool
isPrefixOf x (y:ys) = if length x == 0
then True
else if head x == y
then isPrefixOf (tail x) ys
else False
2. Die folgenden Funktionen funktionieren zwar einwandfrei, können aber einen Feinschliff gebrauchen.
Definieren Sie die Funktionen mit Hilfe von List Comprehension um.
catJusts :: [Maybe a] -> [a]
catJusts []
= []
catJusts (m:ms) = case m of
Just x -> x : catJusts ms
Nothing -> catJusts ms
allPairs :: [a] -> [b] -> [(a,b)]
allPairs xs ys = concatMap (\x -> map (\y -> (x,y)) ys) xs
3. In der Eile wurden im folgenden Code die Typsignaturen vergessen. Geben Sie den allgemeinsten Typ
für die jeweilige Funktion an.
fa [] y = [y]
fa (x:xs) y = case y <= x of
True -> y : x : xs
False -> x : fa xs y
fb a b c d = if a d then b else c
5
fc x y = x
fd [] g z = z
fd (x:xs) g z = fd xs g (g z x)
fe x y Nothing = y
fe x y (Just v) = x v
10 Punkte
Aufgabe 4 - Fahrkarten, bitte!
In dieser Aufgabe sollen Sie einen Datentyp (sowie jegliche Hilfstypen, die Sie benötigen) für Fahrkarten sowie
Funktionen auf diesem Datentyp definieren.
Geben Sie in allen Fällen die Typsignatur für definierte Funktionen an.
1) Definieren Sie einen Datentyp Ticket für Fahrkarten. Es sollen dabei verschiedene Arten von Fahrkarten
repräsentiert werden können:
• Eine Zugfahrtkarte von der Startstadt zur Zielstadt; Erste oder Zweite Klasse ist möglich
• Eine Busfahrtkarte von der Startstadt zur Zielstadt (sowas wie ein Fernbus)
Aus Gründen der Typsicherheit sollen Städte und Ticketklassen dabei nicht als String repräsentiert
werden. Als Städte sollen dabei Hamburg, Kiel, Frankfurt und Freiburg möglich sein.
2) Geben Sie zwei Werte für den Datentyp Ticket an. Dabei soll jede Fahrkartenart (Zug- und Busticket)
einmal verwendet werden.
3) Eine Reise wird durch eine Liste von Fahrtkarten repräsentiert: type Journey = [Ticket]. Eine Reise
ist genau dann valide, wenn für je zwei aufeinanderfolgende Tickets in der Liste gilt: die Zielstadt der
ersten Fahrkarte ist die Startstadt der zweiten Fahrkarte. Definieren Sie nun ein Prädikat valid ::
Journey -> Bool{haskell}, das erfüllt ist, wenn eine Reise gültig ist. Sinnvollerweise besteht eine Reise
aus einer mindestens zwei-elementigen Liste von Fahrtkarten; die Randfälle (leere und einelementige
Liste) sollen das Ergebnis True liefern.
4) Geben Sie eine valide Show-Instanz für Ihren Datentyp Ticket an, der die Informationen des Datentypens
schemenhaft wie folgt als String darstellt:
Fahrzeug (Klasse): Startstadt ~> Zielstadt
Hinweis: Bedenken Sie, dass Datentypen, die innerhalb ihrer Definition vorkommen, auch eine ShowInstanz benötigen, wenn diese nicht schon vorhanden sein sollte.
10 Punkte
Aufgabe 5 - Nichtdeterministisches Sortieren
Das folgende Prologprogramm definiert ein Prädikat bubble(XS,YS). Das zweite Argument stellt dabei die
aufsteigend sortierte Liste des ersten Arguments dar und wird dabei mit Hilfe eines nichtdeterministischen
Bubblesort-Algorithmus sortiert.
bubble(L, R) :- swap(L, N), bubble(N, R).
bubble(L, L).
swap([A,B|L], [B,A|L]) :- B < A.
swap([A|L], [A|N]) :- swap(L, N).
1. Zunächst betrachten wir das Prädikat swap etwas genauer. Bearbeiten Sie eine der folgenden Aufgaben,
um die Anfrage ?- swap([4,3,2],Xs). zu beweisen.
i) Geben Sie für die Anfrage den entstehenden SLD-Baum an, der durch die SLD-Resolution aus der
Vorlesung entsteht.
6
ii) Beweisen Sie die Anfrage mit der Auswertungsstrategie von Prolog.
2. Betrachten wir folgende Anfrage von bubble:
?- bubble([2,3,1],X).
X = [1, 2, 3];
X = [2, 1, 3];
X = [2, 3, 1].
Durch die nichtdeterministische swap-Funktion und der Überlappung der ersten und zweiten Regel in
bubble erhalten wir durch Backtracking insgesamt drei Lösungen, da jede teilsortierte Liste in Relation
mit der Originalliste steht.
Modifizieren Sie die obige Implementierung durch das Einfügen eines Cut-Operators (!) so, dass das
Prädikat bubble letztendlich nur noch in Relation mit ihrer sortierten Liste steht. Es dürfen keine
anderen Änderungen vorgenommen werden.
So soll z.B. die Anfrage ?- bubble([2,3,1],X) nur noch X = [1,2,3] als Ergebnis liefern.
Skizzieren Sie dabei grob, in welchem Schritt der Cut-Operator in der Anfrage auftaucht und in welchem
Schritt welche Berechnungen wegfallen.
Aufgabe 6 - Die Philosophie ist eine Art Rache an der Wirklichkeit – Friedrich Nietzsche
10 Punkte
Betrachten Sie die folgende, fehlerhafte Implementierung der dinierenden Philosophen mit Deadlockvermeidung durch “Ziehen mit Zurücklegen”.
1. Diese Implementierung beinhaltet drei Fehler, die zu einem fehlerhaften Verhalten führen können. Geben
Sie für jeden dieser Fehler
• die fehlerhafte Codestelle,
• eine Erklärung, warum diese Stelle fehlerhaft ist
• sowie eine fehlerbereinigte Implementierung an.
Gehen Sie davon aus, dass die Sticks den Philosophen korrekt übergeben werden.
1
2
3
public class Stick {
private boolean isUsed;
public Stick() { isUsed = false; }
4
public synchronized void put() {
notify();
}
5
6
7
8
public synchronized void take() {
while (isUsed) {
try { notify(); } catch (InterruptedException e) {}
}
isUsed = true;
}
9
10
11
12
13
14
15
public synchronized boolean isUsed() { return isUsed; }
16
17
16
17
}
public class Philosopher {
private Stick left, right;
18
19
public Philosopher(Stick l, Stick r) { left = l; right = r; }
7
20
public void philosophize() {
while (true) {
System.out.println("Thinking.");
21
22
23
24
// Ziehen mit Zurücklegen
right.take();
if (left.isUsed()) {
right.put();
synchronized (left) {
try { left.wait(); } catch (InterruptedException e) {}
}
}
left.take();
System.out.println("Eating.");
left.put();
right.put();
}
25
26
27
28
29
30
31
32
33
34
35
36
37
}
38
}
39
40
}
8
Herunterladen