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);;