if ... then ... else ...
(benutzen Sie stattdessen if'
sofern importiert)[(i,j) | i <- [1,2], j <- [3,4]]
Bei der spuren-basierten Programmierung wird ein Programm schrittweise aus Ausführungsspuren konstruiert. Zuerst wird der zu implementierende Algorithmus per Hand ausgeführt. Diese Ausführung wird dann in eine Tabelle übersetzt, bei welcher die Spalten mit den Variablen des Programms beschriftet sind und jede Zeile einem Ausführungsschritt entspricht. Anschließend müssen Operationen angegeben werden, welche die Veränderung der Werte in jeder Zeile beschreiben. Daraus kann dann der Kontrollflussgraph des Programms abgeleitet werden. Zum Schluss muss für jede Operation ein Entscheidungsbaum angegeben werden, der beschreibt, mit welcher Operation das Programm als nächstes fortfahren soll. Die Struktur von Programmen, welche mithilfe dieser Methode entwickelt werden, entspricht dem Aufbau von sp-Programmen.
Ab Teil 2 geht es darum, gegebene sp-Programme zu vervollständigen. Gewisse Informationen, wie z.B. Ausführungsspuren in Form von Tabellen, sind dabei schon gegeben. Sie können Teile der Methode anwenden, um die Lücken entsprechend auszufüllen.
type Operation a = (a -> a) type Predicate a = (a -> Bool)
a
repräsentiert.if2 :: (Predicate a) -> (Operation a) -> (Operation a) while :: (Predicate a) -> (Operation a) -> (Operation a) repeat :: Int -> (Operation a) -> (Operation a) foreach :: [b] -> (Operation (a,b)) -> (Operation a)
if2
bekommt ein Prädikat arg1
und eine Operation arg2
als Parameter und soll folgende Operation zurückgeben. Falls arg1
wahr ist, dann wird arg2
ausgeführt, ansonsten bleibt der Zustand des Programms unverändert.while
soll folgende Operation zurückgeben. Solange das Prädikat arg1
wahr ist, wird die Operation arg2
wiederholt ausgeführt. Wenn das Prädikat arg1
nicht wahr ist, dann wird der Zustand des Programms zurückgegeben.repeat
soll folgende Operation zurückgeben. Es wird arg1
-mal arg2
ausgeführt (arg1
≥ 0).foreach
soll folgende Operation zurückgeben. Für jedes Element der Liste arg1
soll die Operation arg2
ausgeführt werden. Die Operation arg2
bekommt zusätzlich zum Programmzustand auch das Element der Liste übergeben. Die Liste wird in ihrer natürlichen Reihenfolge durchlaufen.
tailRecursion :: (Operation a) -> (Operation a) -> (Predicate a) -> (Operation a)
tailRecursion
nur mithilfe der Funktionen while
und (.)
(Komposition (f . g) x = f(g(x))
). Es soll gelten arg1
\(=g\), arg2
\(=h\) und arg3
\(=p\).
primitiveSort lst = (while (not.isEmpty.?) ?) lst
primitiveSort
indem Sie die '?' in obigem Code-Snippet entsprechend ersetzen.
bubbleSort :: (Ord a) => (Operation [a]) swapPred :: (Ord a) => (Predicate ([a],Int)) swapOp :: (Ord a) => (Operation ([a],Int))
swapPred
und swapOp
entsprechend definiert werden.
bubbleSort lst = program ? where n = length lst program = (repeat ? (foreach ? (if2 ? ?) ) )
Die vorherige Version von BubbleSort kann mithilfe folgender zwei Beobachtungen erweitert werden, sodass der Algorithmus in bestimmten Fällen nach weniger Schritten terminiert.
Wenn während einer Iteration keine zwei Elemente vertauscht werden, dann ist die Liste sortiert.
Nach der ersten Iteration ist das größte Element auf dem letzten Platz. Nach der zweiten Iteration ist das zweitgrößte Element auf dem vorletztem Platz, usw. Das heißt, nach der ersten Iteration braucht das letzte Element der Liste nicht mehr betrachtet zu werden, nach der zweiten Iteration brauchen die letzten zwei Elemente der Liste nicht mehr betrachtet werden, usw.
Implementieren Sie den erweiterten Algorithmus indem Sie die '?' in der Datei AB3_2/bubbleSort.sp
entsprechend ersetzen. Die Traces, welche beim Aufruf der Funktion main
erzeugt werden, müssen identisch sein zu denen in AB3_2/reference
(dies wird beim Aufruf von main
getestet).
\(a\)=5, \(b\)=16 | ||
---|---|---|
\(i\) | \(c\) | \(r\) |
16 | 5 | 0 |
8 | 10 | |
4 | 20 | |
2 | 40 | |
1 | 80 | 80 |
\(a\)=7, \(b\)=11 | ||
---|---|---|
\(i\) | \(c\) | \(r\) |
11 | 7 | 7 |
5 | 14 | 21 |
2 | 28 | |
1 | 56 | 77 |
Die Zahl \(c\) wird auf \(r\) addiert, falls \(i\) ungerade ist. Die Sequenz der Werte, welche \(i\) annimmt, beschreiben die Binärdarstellung von \(b\) (gerade=0, ungerade=1).
Implementieren Sie den obigen Algorithmus indem Sie die '?' in der Datei AB3_3/egyptMult.sp
entsprechend ersetzen. Die Traces, welche beim Aufruf der Funktion main
erzeugt werden, müssen identisch sein zu denen in AB3_3/reference
(dies wird beim Aufruf von main
getestet). Sie können davon ausgehen, dass \(a, b \geq 0\).
a
und b
gegeben und es soll geprüft werden, ob concat a == concat b
. Zum Beispiel gilt das für a = ["ab","cde"]
und b = ["a","bc","de"]
, da beide "abcde"
ergeben. Allerdings sind die Strings in a
und b
vertraulich. Deshalb kann nur indirekt auf die beiden Listen zugegriffen werden:
coa :: CSMInstance -> Int cob :: CSMInstance -> Int lena :: CSMInstance -> Int -> Int lenb :: CSMInstance -> Int -> Int ssc :: CSMInstance -> Int -> Int -> Int -> Int -> Int -> Bool
coa
(= cardinality of a) gibt die Anzahl der Strings in a
zurück (analog dazu cob
für b
).lena
(= length of element of a) gibt die Länge des (arg1
+1)-ten Strings in a
zurück (analog dazu lenb
für b
).ssc
(= substring call) ist wahr gdw. wenn zwei Teilstrings aus a
und b
gleich sind. Genauer gesagt: ssc (CSMInstance a b) ca cb oa ob l
ist wahr gdw. die ersten l
Zeichen der Teilstrings a'
und b'
gleich sind, wobei a'
der Teilstring vom (ca
+1)-ten String aus a
ist, welcher ab dem (oa
+1)-tem Zeichen anfängt (analog dazu b'
für cb
).AB3_4/csmAlg.sp
entsprechend ersetzen. Die Traces, welche beim Aufruf der Funktion main
erzeugt werden, müssen identisch sein zu denen in AB3_4/reference
(dies wird beim Aufruf von main
getestet). Außerdem müssen Sie Funktionen in der Datei AB3_4.hs
implementieren, mit welchen Sie die Prädikate p_eqlen
und p_empty
ausdrücken können.
Das Josephus-Problem ist wie folgt definiert (siehe auch Numberphile (Video)). Es stehen \(n\) Personen im Kreis, welche von \(1\) bis \(n\) nummeriert sind. Am Anfang entfernt Person \(1\) ihren Nachbarn (Person \(2\)) aus dem Kreis. Danach entfernt der neue Nachbar von \(1\) (Person \(3\)) seinen Nachbarn (Person \(4\)) aus dem Kreis. Die nächste Person, die jemanden entfernt, ist dann \(5\). Dies wird solange wiederholt, bis nur noch eine Person übrig ist. Wie lautet die Nummer dieser Person?
Implementieren Sie den obigen Algorithmus indem Sie die '?' in der Datei AB3_5/josephus.sp
entsprechend ersetzen. Die Traces, welche beim Aufruf der Funktion main
erzeugt werden, müssen identisch sein zu denen in AB3_5/reference
(dies wird beim Aufruf von main
getestet).