Funktionale Programmierung
Zentrale Strukturen und Algorithmen
Was kenne ich bisher?
Rekursive Funktionsaufrufe (linear, Einstellig)
Rekursive Datenstrukturen (Listen, Bäume)
Was ist zu tun?
1.)
Vertiefung der Rekursion über Funktion
2.)
Terminierung rekursiver Funktionen
3.)
Verallgemeinerung / Vertiefung rek. Datenstrukturen: Polymorphie, Pattern
matching
4.)
Korrektheit von Implementierungen
5.)
Wichtigsten rekursiv zu formulierenden Algorithmen
1.) Vertiefung rekursiver Funktionen
Definition (informell): Wir sprechen von Rekursion, wenn auf der rechten Seite einer
Funktionsdefinition der Form let f1 = (m x) n: E im Ausdruck E die zu definierende Fkt. F1
auftritt.
m
x
n
E
Sorte
Parametersatz
Sorte der Rückgabewerte
Berechnungsausdruck
Allgemein: Sei
let f1 = ( m1 x1,…, mnxn)n1:E1
eine Funktionsdeklaration. Dann ist f1 rekursiv, wenn der Bezeichner f1 im Ausdruck E1
enthalten ist (also: rein syntaktisch prüfbar!)
Lineare Rekursion:
Der Bezeichner f1 tritt in E1 innerhalb einer Verzweigung einer Fallunterscheidung höchstens
ein mal auf.
Beispiel:
a) der durch den euklidischen Algorithmus definierte GGT ist linear rekursiv
b) Die Funktion lowerpart, welche die Elemente einer liste l berechnet, die kleiner als
ein best. Element el sind, ist linear
let rec lp (el, liste) = match (el, liste) with
| (el, []) []
| (el, a :: rest) when (a<el) a::(lp el rest)
| (el, a:: rest) lp el rest
;;
Nichtlineare Rekursion:
Der Bezeichner f1 tritt in E1 innerhalb eines Zweiges einer Fallunterscheidung mind. 2 mal
auf.
Beispiel:
a) Sortieralgorithmus Quicksort (Moore)
Sortieren durch Zerlegen
o Wähle ein beliebiges Element x aus der zu sortierenden Menge M aus. (x heißt
auch „Pirot- Element“)
o Zerlege M in die Mengen
M< = { L| L<x}, M= = {c| c =x}, M > = {u| u>x}
o Sortiere M<, M=, M >
o Die sortierte Menge M`ergibt sich dann durch Konkatenation wie folgt:
o M`= M< = M= = M >, wenn auf M eine Ordnung existiert.
Implementierung Quicksort:
Let rec quicksort liste = match liste with
| [] []
| [e] [e]
| (e::rest) quicksort(lp(e,liste)
@ ep (e, liste)
@ quicksort (up(e,liste))
;;
Bemerkung: „ep“ und „up“ sind analog zu „lp“ der equalpart und upponpart
b) Permutation auf Sequenzen
( Erinnerung: Kryptographie, Kombinatorik, Zufallszahlen- Erzeugung)
Permutationen sind bijektive Abbildungen
: [1,…n] [[1,…,n]]
Gesucht:
Rechenvorschrifft, die zu einer Sequenz s die Sequenz aller
Permutationen von s liefert. Zum Beispiel;
perm([abc]) = ([abc],[acb],[bac],[bca],[cab],[cba])
Lösungsvorschläge für Berechnung der Permutation:
1.) systemantsiches Vorgehen: „Durchschieben“ des Elements am Kopf der Liste auf
alle Listenpositionen und Permutation der aus den anderen Elementen der
gebildeten Liste.
n=1
a
n=2
ab
ba
n=3
abc
acb
bac
cab
bca
cba
n=4
abcd
abdc
acbd
adbc
acdb
adcb
…
…
„a“ durchschieben je
„bc permutieren.
Wenn alle Permutationen des Listenrestes bekannt sind, folgt die Permutation durch
Durchschieben des Listenkopfes durch alle (Teil)Permutationen
Formulierung unter Nutzung von Hilfsfkt. wie folgt:
a) Funktion „StickOn“: ’a x list x list list x list
Platziert ein Element x an den Anfang jedes Elements jeder Teilliste innerhalb
einer übergebenen Gesamtliste.
let rec stickOn x l = match l with
| [] []
| (h :: t) (x :: h) :: stickOn x t;;
Aufruf: stickOn 3 [[1;4;5];[4;1;5];[4;5;1]];;
b) Funktion PutInAll ’a x list list x list
let rec putInAll x l = match l with
| [] [[x]]
| (h::t) (x::l) :: stickOn h (putInAll x t);;
c) Schließlich wird eine Funktion benötigt, die unter Verwendung von putInAll ein
Element x durch die Liste von Teilpermutationen schiebt und daraus die
Ergebnisliste erzeugt.
’a x list x list list x list
let rec permuteAux x l = match l with
| [] -> []
| (h::t) -> (putInAll x h) @ (permuteAux x t) ;;
d) Damit möglich, Permutation anzugeben :
let rec permute l = match l with
| [] -> [[]]
| (h :: t) -> permuteAux h (permute t);;