Übung Programmierung SS 10

Werbung
Wolfgang Hönig / Andreas Ecke
SS 10
Übung Programmierung
2. Übung: Zusatzaufgaben
Im folgenden wollen wir die Funktion mergesort in Haskell programmieren. Mergesort
basiert genau wie Quicksort auf dem Divide and Conquer-Prinzip und arbeitet folgendermaßen: Zuerst wird die Liste in 2 Teillisten aufgespaltet. diese werden rekursiv sortiert
und im Anschluß daran wieder so zusammengefügt, dass die Gesamtliste ebenfalls sortiert ist. Dazu müssen beim Zusammenfügen jeweils immer die Kopfelemente der Teillisten
verglichen werden, um festzustellen, welches Element das nächst kleinste ist. Als erstes
benötigen wir die Funktion split, die uns eine Liste in 2 ca. gleichgroße Teillisten aufteilt
(z.B. split :: [Int] −> ([Int],[Int])).
Eine triviale Funktion, die das realisiert, würde so aussehen:
split l = let halflen = div (length l) 2 in (take halflen l , drop halflen l )
Dabei gibt take n l die ersten n Elemente der Liste l zurück und drop n l entsprechend
alle Elemente aus l außer den ersten n. Diese Funktion ist jedoch sehr ineffektiv, da sie die
Liste l drei mal durchlaufen muss: einmal um die Länge der Liste zu ermitteln, einmal um
die ersten n Elemente herauszufinden und ein drittes Mal, um die restlichen Elemente zu
ermitteln. Die Aufgabe ist es deswegen, eine Funktion zu schreiben, die diese Aufteilung
einer Liste in nur einem Durchlauf schafft. Erstelle anschließend aus der Funktion split
und der Funktion merge aus Aufgabe 2.b) (Aufgabensammlung 11.21) eine Funktion
mergesort, die eine Liste von Zahlen aufsteigend sortiert.
Hinweise:
1. Die Listenelemente müssen nicht genau so aufgeteilt werden: Eine Möglichkeit wäre
auch, die Elemente immer abwechselnd auf Liste 1 und 2 aufzuteilen.
2. Da man in Haskell nicht 2 Rückgabewerte hat, gibt die Funktion split ein 2er-Tupel
aus 2 Listen zurück. Um auf den ersten Wert eines solchen Tupels zuzugreifen, kann
die Funktion fst genutzt werden, für den 2ten Wert die Funktion snd. (Die Signatur
von fst ist: fst :: (a, b) −> a).
Lösungen
Die Funktion split teilt hier die Elemente der Eingabeliste abwechselnd auf die beiden
Ausgabelisten aus. Damit klar ist, auf welche Liste das nächste Element kommt, wird
noch ein Parameter z übergeben, welcher entweder den Wert 0 oder 1 hat:
1
2
3
4
5
6
7
8
9
10
11
12
13
s p l i t : : [ Int ] −> Int −> ( [ Int ] , [ Int ] )
split [ ] z = ( [ ] , [ ] )
split (a : x) z
| z == 0 = ( a : ( f s t t ) , snd t )
| otherwise = ( f s t t , a : ( snd t ) )
where t = s p l i t x (1−z )
merge
merge
merge
merge
: : [ Int ] −> [ Int ] −> [ Int ]
[] r = r
l [] = l
(a : x) (b : y)
| a < b = a : ( merge x ( b : y ) )
| otherwise = b : ( merge ( a : x ) y )
Die Funktion mergesort ist nun relativ einfach. Sie splittet die Liste mit der Funktion
split in 2 Teile, sortiert diese rekursiv und fügt die beiden Listen dann mit der Funkti-
on merge wieder zusammen. Der Rekursionsabbruch muss in diesem Fall schon bei der
einelementigen Liste erfolgen, um eine Endlosschleife zu verhindern.
1
2
3
4
5
mergesort
mergesort
mergesort
mergesort
: : [ Int ] −> [ Int ]
[] = []
[a] = [a]
l = merge ( m e r g e s o r t ( f s t t ) ) ( m e r g e s o r t ( snd t ) )
where t = s p l i t l 0
Herunterladen