Lösungen

Werbung
1 - Nebenläufigkeit in Java
Implementierungsfehler
1. Fehler in Klasse Stick, Zeile 5 Beim Zurücklegen des Sticks fehlt der Aufruf von notify(), ohne diesen
Aufruf wird ein einmal wartender Philosoph nie aufgeweckt.
Richtig wäre also:
public synchronized void put() {
isUsed = false;
notify();
}
2. Fehler in Klasse Stick, Zeile 8 Hier wird in der Abfrage if statt while benutzt. Dies kann dazu führen
dass ein Philosoph 2 zunächst wartet da sein Nachbar 1 isst, dieser den Stick dann zurückgibt und Philosoph 1
aufweckt, sich den Stick aber auch sofort wieder nimmt (da er sehr hungrig ist) und dann beide Philosophen
denselben Stick haben.
Richtig wäre also:
public synchronized void take() {
while (isUsed) {
try { wait(); } catch (InterruptedException e) {}
}
isUsed = true;
}
3. Fehler in Klasse Philosopher, Zeile 28 - 30 Das wait auf dem rechten Stick macht zum einen keinen
Sinn, da das anschließende right.take() unter Umständen ebenfalls warten würde. Zum anderen wird auch
noch der linke Stick behalten.
Richtig wäre also:
left.take();
if (right.isUsed()) {
left.put();
} else {
right.take();
System.out.println("Eating.");
}
left.put();
right.put();
Hauptprogramm
1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
int philCount = 3;
Stick[] sticks = new Stick[philCount];
for (int i = 0; i < philCount; ++i) {
sticks[i] = new Stick();
}
for (int i = 0; i < philCount; ++i) {
new Philosopher(sticks[i], sticks[(i + 1) % philCount]).start();
}
}
1
Die Klasse Stick bleibt unverändert, die Klasse Philosopher muss nun aber noch von der Klasse Thread
erben
public class Philosopher extends Thread
und die Methode philosophize muss in run umbenannt werden.
Alternativ könnte die Klasse Philosopher auch das Interface Runnable implementieren
public class Philosopher implements Runnable
dann müssen die Philosophen aber beispielsweise mittels
new Thread(new Philosopher(sticks[i], sticks[(i + 1) % philCount])).start();
gestartet werden. Auch hier muss die Methode philosophize in run umbenannt werden.
2 - Listen in Haskell
Rekursive Variante:
1
2
any1 _ []
= False
any1 p (x:xs) = p x || any1 p xs
Variante mit foldr:
3
any2 p = foldr (\x b -> p x || b) False
oder
4
any2' p xs = foldr (||) False (map p xs)
Variante mit List Comprehension:
5
any3 p xs = not $ null [ () | x <- xs, p x ]
Die Typsignatur ist jeweils:
6
any1, any2, any2', any3 :: (a -> Bool) -> [a] -> Bool
Zum Schluss der Test auf die Quadratzahleigenschaft:
7
8
isSquare :: Integer -> Bool
isSquare n = any (\x -> x * x == n) [ 2 .. n `div` 2 ]
3 - Datentypen in Haskell
Teil a)
1
2
-- |Natural numbers with 0
data NatN = Zero | Pos Nat
3
4
5
-- |Natural numbers without 0 in binary representation
data Nat = IHi | O Nat | I Nat
6
7
8
zero :: NatN
zero = Zero
9
10
11
seven :: NatN
seven = Pos (I (I IHi))
12
13
14
fortytwo :: NatN
fortytwo = Pos (O (I (O (I (O IHi)))))
2
Teil b)
15
data A a = B () | A (a -> a)
16
17
value1, value2, value3 :: A Bool
18
19
20
21
value1 = B ()
value2 = A id
value3 = A not
Teil c)
22
23
24
data Exp = Num Int
| Add Exp Exp
| Mul Exp Exp
Die einfache Variante:
25
26
27
28
instance Show Exp where
show (Num
n) = show n
show (Add e1 e2) = "(" ++ show e1 ++ " + " ++ show e2 ++ ")"
show (Mul e1 e2) = "(" ++ show e1 ++ " * " ++ show e2 ++ ")"
Die bessere Variante:
29
30
31
32
33
34
instance Show Exp where
show (Num
n) = show n
show (Add e1 e2) = show
e1
show (Mul e1 e2) = showParen e1
where showParen e@(Add _ _) =
showParen e
=
++ " + " ++ show
e2
++ " * " ++ showParen e2
"(" ++ show e ++ ")"
show e
4 - Do-Notation in Haskell
Teil a)
1
2
3
4
5
6
7
8
readNonEmpty :: IO String
readNonEmpty =
getLine >>= \str ->
(if null str
then putStrLn "Please insert a non-empty string" >>
readNonEmpty
else return str) >>= \nonEmpty ->
return nonEmpty
Teil b)
9
10
11
12
13
14
15
readNonEmpty2 :: IO String
readNonEmpty2 = do
str <- getLine
if null str
then do putStrLn "Please insert a non-empty string"
readNonEmpty2
else return str
3
5 - Resolution
Wir erstellen zunächst den SLD-Baum:
?- delete(1, [1,0+1], Xs).
/
\
Regel (2)
/
\
Regel (3)
{ X1 -> 1
/
\
{ X1 -> 1
, Y1 -> 1
/
\
, Y1 -> 1
, Xs1 -> [0+1] /
\
, Xs1 -> [0+1]
, Xs -> [0+1] /
\ , Xs -> [1|Ys1]
}
/
\ }
/
\
?- 1 = 1.
?- delete(1, [0+1], Ys1).
|
/
\
{} |
Regel (2)
/
\
Regel (3)
|
{ X2 -> 1
/
\
{ X2 -> 1
?- .
, Y2 -> 0+1 /
\
, Y2 -> 0+1
% Erfolg mit Xs = [0+1] , Xs2 -> [] /
\
, Xs2 -> []
, Ys1 -> [] /
\ , Ys1 -> [0+1|Ys2]
}
/
\ }
/
\
?- 1 = 0+1.
?- delete(1, [], Ys2).
% Fehlschlag
|
| Regel (1)
| { Ys2 -> [] }
|
?- .
% Erfolg mit Xs = [1, 0+1]
Berechnung der Lösung nach dem Suchverfahren von Prolog:
?- delete(1, [1,0+1], Xs). (*)
|- {X1 -> 1, Y1 -> 1, Xs1 -> [0+1], Xs
?- 1 = 1.
|- {}
?- .
Ausgabe: Xs = [0+1]
Rücksetzen, 3. Regel bei (*)
|- {X1 -> 1, Y1 -> 1, Xs1 -> [0+1], Xs
?- delete(1, [0+1], Ys1). (**)
|- {X2 -> 1, Y2 -> 0+1, Xs2 -> [], Ys1
?- 1 = 0+1.
Fehlschlag
Rücksetzen, 3. Regel bei (**)
|- {X2 -> 1, Y2 -> 0+1, Xs2 -> [], Ys1
?- delete(1, [], Ys2).
|- {Ys2 -> []} (1. Regel)
Ausgabe: Xs = [1,0+1]
-> [0+1]} (2. Regel)
-> [1|Ys1]}
-> []} (2. Regel)
-> [0+1|Ys2]}
Um nur das erste Vorkommen des 1. Parameters aus der Liste zu entfernen, fügen wir einen Cut bei der 2.
Regel hinzu:
1
2
3
deleteFirst(_, []
, []
) .
deleteFirst(X, [Y|Xs], Xs
) :- X = Y, !.
deleteFirst(X, [Y|Xs], [Y|Ys]) :- deleteFirst(X, Xs, Ys).
4
Um alle Vorkommen des 1. Parameters aus der Liste zu entfernen, müssen wir die 2. Regel anpassen, s dass nach
dem Löschen des 1. Vorkommens die Restliste nach weiteren Vorkommen durchsucht wird. Außerdem muss
in der 3. Regel zusätzlich sichergestellt werden, dass der 1. Parameter von delete und das 1. Listenelement
ungleich sind:
4
5
6
deleteAll(_, []
, []
) .
deleteAll(X, [Y|Xs], Ys
) :- X = Y, deleteAll(X, Xs, Ys).
deleteAll(X, [Y|Xs], [Y|Ys]) :- X \= Y, deleteAll(X, Xs, Ys).
6 - Verwandtschaftsbeispiel in Prolog
Der Vollständigkeit halber betrachten wir zunächst noch einmal die vorgegebenen Verwandtschaftsbeziehungen
aus der Vorlesung:
1
2
3
4
ehemann(christine,
ehemann(maria
,
ehemann(monika
,
ehemann(angelika ,
heinz ).
fritz ).
herbert).
hubert ).
5
6
7
8
9
10
11
mutter(herbert ,
mutter(angelika,
mutter(hubert ,
mutter(susanne ,
mutter(norbert ,
mutter(andreas ,
christine).
christine).
maria
).
monika
).
monika
).
angelika ).
12
13
14
15
16
17
18
maennlich(heinz ).
maennlich(fritz ).
maennlich(herbert).
maennlich(hubert ).
maennlich(norbert).
maennlich(andreas).
Um alle Großmütter zu bestimmen, müssen wir alle Mütter von Personen ermitteln, die selbst Eltern sind.
Daher definieren wir uns zunächst ein Hilfsprädikat vater{.prolog} und dann eine Relation elternteil:
19
vater(Kind, Vater) :- mutter(Kind, Mutter), ehemann(Mutter, Vater).
20
21
22
elternteil(Kind, Elternteil) :- mutter(Kind, Elternteil).
elternteil(Kind, Elternteil) :- vater(Kind, Elternteil).
23
24
oma(Oma) :- elternteil(_, Elternteil), mutter(Elternteil, Oma).
Alle Onkel (außer den angeheirateten) einer Person erhalten wir, indem wir alle Brüder der Eltern dieser Person
bestimmen. Dazu definieren wir uns zunächst eine Relation für Geschwister. Geschwister sind Personen, die
dieselbe Mutter haben. Zusätzlich beachten muss man aber noch, dass es sich bei den beiden Geschwistern um
unterschiedliche Personen handeln muss.
25
26
27
geschwister(Geschwister1, Geschwister2) :mutter(Geschwister1, Mutter), mutter(Geschwister2, Mutter),
Geschwister1 \= Geschwister2.
Alle Brüder einer Person erhalten wir, indem wir zusätzlich nur männliche Geschwister betrachten:
28
bruder(Person, Bruder) :- geschwister(Person, Bruder), maennlich(Bruder).
29
30
31
onkel(Person, Onkel) :elternteil(Person, Elternteil), bruder(Elternteil, Onkel).
5
Um die Liste aller Kinder einer Person zu bestimmen, können wir erneut auf das Prädikat elternteil
zurückgreifen. Allerdings wollen wir nun nur eine nicht-deterministische Lösung erhalten. Daher kapseln wir
den Nicht-Determinismus mit Hilfe des Metaprädikats findall:
32
kinderVon(Person, Kinder) :- findall(Kind, elternteil(Kind, Person), Kinder).
6
Herunterladen