Algorithmen und Programmierung II
Übungsblatt 10 - Musterlösung
Aufgabe 1
void insertIterativ(String w) {
Spelling current = this;
loop: for (;;) {
if (current.next == null || current.next.word.compareTo(w) > 0) {
current.next = new Spelling(w, current.next);
} else if (current.next.word.equals(w)) {
;
} else {
current = current.next;
continue loop;
}
return;
}
}
Aufgabe 2
a) Java iterativ
static float kbJ(int[] a) {
float result = 0;
for (int i = a.length - 1; i >= 0; i--)
result = 1 / (a[i] + result);
return result;
}
b) Haskell rekursiv
kbH :: [Integer] -> Float
kbH [] = 0
kbH (x:xs) = 1 / (fromInteger x + kbH xs)
c) Haskell endrekursiv
kbHe :: Float -> [Integer] -> Float
kbHe acc [] = acc
kbHe acc xs = kbHe (1 / (fromInteger (last xs) + acc)) (init xs)
d)
1. Schritt: Java endrekursiv, entwickelt aus c)
static float kbJendrek(int[] a, int last, float acc) {
if (last < 0) return acc;
return kbJendrek(a, last-1, 1 / (a[last] + acc));
}
2. Schritt: Entrekursivierung von kbJendrek
nach Abschnitt 2.4.1
static float kbJ2(int[] a, int last, float acc) {
loop: for(;;) { if (last < 0) return acc;
acc = 1 / (a[last] + acc);
last--;
continue loop;
}
}
3. Schritt: syntaktisch optimiert
static float kbJ3(int[] a, int last, float acc) {
for(; last >= 0; last--)
acc = 1 / (a[last] + acc);
return acc;
}
e) Die zweite Version unter d) entspricht der Struktur des
Transformationsschemas. Bei der dritten Version (kbJ3) wurde der Quelltext etwas
"aufgeräumt". Diese dritte Variante ist mit der unter a)
entwickelten Version praktisch identisch. Einziger Unterschied sind die
Hilfsparameter last und acc, die aus der schematischen
Transformation des funktionalen in den imperativen Code stammen (Liste
vs. Feld). Im nächsten Schritt würde man eine Einbettung
durchführen, acc als
lokale Variable einführen und den Startwert von last aus der Länge des
Feldes a gewinnen, so
dass man genau die unter a) entwickelten Version erhält.
f) Java iterativ
static float sqrt2J(float eps) {
float res = 0,
old = Float.MAX_VALUE;
while (Math.abs(old - res) > eps) {
old = res;
res = 1 / (2 + res);
}
return res + 1;
}
g) Haskell rekursiv, entwickelt aus f)
Nach Abschnitt 2.4.3 überführt die Schleife
while( C(z) ) { z=F(z); }
einen Anfangszustand init
in den Endzustand
while c f init
Den relevanten Zustand bilden hier der Parameter eps der Java-Methode und die
beiden lokalen float-Variablen
res und old. Der Anfangszustand ergibt sich aus der
Initialisierung der Variablen als Tupel (eps, 0, Float.MAX_VALUE),
wobei der dritte Wert um mindestens eps vom zweiten abweichen muss,
damit der Algorithmus korrekt funktioniert. In Haskell wird daher
alternativ (eps, 0, eps+1)
verwendet.
Die Funktionen c und f ergeben sich aus der
Java-Version wie folgt:
c (eps, res, old) = abs (old-res) > eps
f (eps, res, old) = (eps, 1/(2+res), res)
Unter Verwendung der while-Funktion
aus Abschnitt 2.4.3 ergibt sich die gesuchte Haskell-Funktion wie
folgt. Ergebnis ist die um 1 erhöhte zweite Komponente des
Zustands-Tupels:
sqrt2H :: Float -> Float
sqrt2H eps = res + 1
where (_, res, _) = while c f (eps, 0, eps+1)
c (eps, res, old) = abs (old-res) > eps
f (eps, res, old) = (eps, 1/(2+res), res)
while :: (t->Bool) -> (t->t) -> t -> t
while cond func init
| cond init = while cond func (func init)
| otherwise = init