3. Funktionales Programmieren 3.1 Grundkonzepte funktionaler Programmierung 3. Funktionales Programmieren Unterabschnitt 3.1.6 3.1 Grundkonzepte funktionaler Programmierung Begriffsklärung: (Modulsystem) Ein Programmmodul fasst mehrere Deklarationen zusammen und stellt sie unter einem Namen zur Verfügung. Module ©Arnd Poetzsch-Heffter TU Kaiserslautern 3. Funktionales Programmieren Ein Programmmodul sollte Programmierern eine Modul-Schnittstelle bereitstellen, die unabhängig von der Implementierung dokumentiert und benutzbar ist. 317 ©Arnd Poetzsch-Heffter 3.1 Grundkonzepte funktionaler Programmierung TU Kaiserslautern 3. Funktionales Programmieren Module in Haskell 318 3.1 Grundkonzepte funktionaler Programmierung Begriffsklärung: (Programm, die 2.) Vereinfacht dargestellt, hat ein Haskell-Modul die Form: module <Modulname > ( <kommagetrennte Liste exportierter Programmelemente > ) where import <Modul1 > ... import <Moduln > <Deklarationen des Moduls > Ein Haskell-Programm (vgl. F. 211) besteht aus einer Menge von Modulen, wobei ein Modul • den Name Main haben muss und • in diesem Modul der Name main deklariert sein muss. Die Programmausführung beginnt mit der Ausführung der Deklaration von main. Dabei gilt: • Die Liste der importierten und exportierten Programmelemente kann leer sein. • Fehlt die Exportliste einschließlich der Klammern, werden alle in dem Modul deklarierten Programmelemente exportiert. ©Arnd Poetzsch-Heffter TU Kaiserslautern 319 ©Arnd Poetzsch-Heffter TU Kaiserslautern 320 3. Funktionales Programmieren 3.1 Grundkonzepte funktionaler Programmierung 3. Funktionales Programmieren Beispiel: (Haskell-Module) 3.1 Grundkonzepte funktionaler Programmierung Beispiel: (Haskell-Module) (2) module Breg where module Alster where import Alster avalue = 7; data Broterwerb = Designer | Maler | Bildhauer data Art = Painting | Design | Sculpture beruf beruf beruf beruf ache Design = False ache _ = True :: Art -> Design Painting Sculpture Broterwerb = Designer = Maler = Bildhauer bflag = (ache Painting ) ©Arnd Poetzsch-Heffter TU Kaiserslautern 3. Funktionales Programmieren 321 3.1 Grundkonzepte funktionaler Programmierung TU Kaiserslautern 3. Funktionales Programmieren Beispiel: (Haskell-Module) (3) 322 3.1 Grundkonzepte funktionaler Programmierung Beispiel: (Modul-Schnittstelle) module Environment (Env , emptyEnv , insertI , insertB , lookUp , delete , IBValue (Intv , Boolv , None) ) where module Main where import Breg emptyEnv :: Env -- leere Bezeichnerumbegung main = print bflag insertI :: String -> Int -> Env -> Env -- ( insertI bez i e) traegt die Bindung (bez ,i) -- in die Umgebung e ein Beachte: Programmelemente aus Alster, z.B. avalue, sind nicht sichtbar in Modul Main. ©Arnd Poetzsch-Heffter ©Arnd Poetzsch-Heffter TU Kaiserslautern insertB :: String -> Bool -> Env -> Env -- ( insertB bez b e) traegt die Bindung (bez ,b) -- in die Umgebung e ein 323 ©Arnd Poetzsch-Heffter TU Kaiserslautern 324 3. Funktionales Programmieren 3.1 Grundkonzepte funktionaler Programmierung 3. Funktionales Programmieren Beispiel: (Modul-Schnittstelle) (2) 3.1 Grundkonzepte funktionaler Programmierung Beispiel: (Modul-Implementierung) -- Modulimplementierung ( Fortsetzung von Environment ) data IBValue = Intv Int | Boolv Bool | None lookUp :: String -> Env -> IBValue -- ( lookUp bez e) liefert den Wert v der ersten gefundenen -- Bindung (bez ,v) mit Bezeichner bez type Env = [ (String , IBValue ) ] delete :: String -> Env -> Env -- ( delete bez e) loescht alle Bindungen (bez ,_) -- mit Bezeichner bez emptyEnv = [] insertI bez i e = (bez ,Intv i):e insertB bez b e = (bez ,Boolv b):e ©Arnd Poetzsch-Heffter TU Kaiserslautern 3. Funktionales Programmieren 325 ©Arnd Poetzsch-Heffter 3.1 Grundkonzepte funktionaler Programmierung TU Kaiserslautern 3. Funktionales Programmieren Beispiel: (Modul-Implementierung) (2) 326 3.1 Grundkonzepte funktionaler Programmierung Unterabschnitt 3.1.7 lookUp bez [] = None lookUp bez ((bz ,val):e) | bez == bz = val | otherwise = lookUp bez e Zusammenfassung von 3.1 delete bez [] = [] delete bez ((bz ,val):e) | bez == bz = delete bez e | otherwise = (bz ,val):( delete bez e) ©Arnd Poetzsch-Heffter TU Kaiserslautern 327 ©Arnd Poetzsch-Heffter TU Kaiserslautern 328 3. Funktionales Programmieren 3.1 Grundkonzepte funktionaler Programmierung 3. Funktionales Programmieren Zusammenfassung von 3.1 3.2 Algorithmen auf Listen und Bäumen Abschnitt 3.2 Begriffe und Sprachmittel wie Ausdruck, Bezeichner, Vereinbarung, Wert, Typ, Muster, Modul, . . . . Wichtige Programmier- und Modellierungskonzepte: Algorithmen auf Listen und Bäumen • Basisdatenstrukturen • rekursive Funktionen • rekursive Datentypen (insbesondere Listen) • Ein- und Ausgabe ©Arnd Poetzsch-Heffter TU Kaiserslautern 3. Funktionales Programmieren 329 ©Arnd Poetzsch-Heffter 3.2 Algorithmen auf Listen und Bäumen TU Kaiserslautern 3. Funktionales Programmieren Algorithmen auf Listen und Bäumen 330 3.2 Algorithmen auf Listen und Bäumen Lernziele: • Intuitiver Algorithmusbegriff • Kenntnis wichtiger/klassischer Algorithmen und Algorithmenklassen Sortieren und Suchen sind elementare Aufgaben, die in den meisten Programmen anfallen. • Zusammenhang Algorithmus und Datenstruktur Verfahren zum Suchen und Sortieren spielen eine zentrale Rolle in der Algorithmik. • Wege vom Problem zum Algorithmus • Implementierungstechniken für Datenstrukturen und Algorithmen (vom Algorithmus zum Programm) Bemerkung: Wir führen in den Bereich Algorithmen und Datenstrukturen ausgehend vom Problem ein. Andere Möglichkeit wäre gemäß der benutzten Datenstrukturen (Listen, Bäume, etc.). ©Arnd Poetzsch-Heffter TU Kaiserslautern 331 ©Arnd Poetzsch-Heffter TU Kaiserslautern 332 3. Funktionales Programmieren 3.2 Algorithmen auf Listen und Bäumen 3. Funktionales Programmieren Unterabschnitt 3.2.1 Sortieren Sortieren ist eine Standardaufgabe, die Teil vieler speziellerer, umfassenderer Aufgaben ist. Sortieren ©Arnd Poetzsch-Heffter 3.2 Algorithmen auf Listen und Bäumen TU Kaiserslautern 3. Funktionales Programmieren Untersuchungen zeigen, dass „mehr als ein Viertel der kommerziell verbrauchten Rechenzeit auf Sortiervorgänge entfällt“ (Ottmann, Widmayer: Algorithmen und Datenstrukturen, Kap. 2). 333 ©Arnd Poetzsch-Heffter 3.2 Algorithmen auf Listen und Bäumen TU Kaiserslautern 3. Funktionales Programmieren Begriffsklärung: (Sortierproblem) 334 3.2 Algorithmen auf Listen und Bäumen Bemerkung: Gegeben ist eine Folge s1 , . . . , sN von sogenannten Datensätzen. Offene Aspekte der Formulierung des Sortierproblem: Jeder Satz sj hat einen Schlüssel kj . Wir gehen davon aus, dass die Schlüssel ganzzahlig sind. • Was heißt, eine Folge ist „gegeben“? Aufgabe des Sortierproblems ist es, eine Permutation π zu finden, so dass die Umordnung der Sätze gemäß π folgende Reihenfolge auf den Schlüsseln ergibt: • Ist der Bereich der Schlüssel bekannt? • Welche Operationen stehen zur Verfügung, um π zu bestimmen? • Was genau heißt „Umordnung“? kπ(1) ≤ kπ(2) ≤ · · · ≤ kπ(N) ©Arnd Poetzsch-Heffter TU Kaiserslautern 335 ©Arnd Poetzsch-Heffter TU Kaiserslautern 336 3. Funktionales Programmieren 3.2 Algorithmen auf Listen und Bäumen 3. Funktionales Programmieren Aufgabenstellung: Aufgabenstellung: (2) Wir benutzen Datensätze folgenden Typs Entwickle eine Funktion type Dataset = (Int , String ) sort :: [ Dataset ] -> [ Dataset ] mit Vergleichsfunktion: so dass das Ergebnis von sort xl für alle Eingaben xl aufsteigend sortiert ist und die gleichen Elemente enthält wie xl (mehrere Einträge mit gleichem Schlüssel sind nicht ausgeschlossen). leq:: Dataset -> Dataset -> Bool leq (kx , dx) (ky , dy) = (kx <=ky) ©Arnd Poetzsch-Heffter TU Kaiserslautern 3. Funktionales Programmieren 3.2 Algorithmen auf Listen und Bäumen 337 ©Arnd Poetzsch-Heffter 3.2 Algorithmen auf Listen und Bäumen TU Kaiserslautern 3. Funktionales Programmieren Aufgabenstellung: (3) 338 3.2 Algorithmen auf Listen und Bäumen Sortieren durch Auswahl (selection sort) Wir betrachten: • Sortieren durch Auswahl (engl. selection sort) Algorithmische Idee: • Sortieren durch Einfügen (engl. insertion sort) • Entferne einen minimalen Eintrag min aus der Liste. • Bubblesort • Sortiere die Liste, aus der min entfernt wurde. • Füge min als ersten Element an die sortierte Liste an. • Sortieren durch rekursives Teilen (quick sort) • Sortieren durch Mischen (merge sort) • Heapsort ©Arnd Poetzsch-Heffter TU Kaiserslautern 339 ©Arnd Poetzsch-Heffter TU Kaiserslautern 340 3. Funktionales Programmieren 3.2 Algorithmen auf Listen und Bäumen 3. Funktionales Programmieren 3.2 Algorithmen auf Listen und Bäumen Sortieren durch Auswahl (selection sort) (2) Sortieren durch Auswahl (selection sort) (3) select :: Dataset -> [ Dataset ] -> Dataset -- Hilfsfunktion : liefert einen minimalen Eintrag der -- Liste bzw. x, falls x minimal delete :: Dataset -> [ Dataset ] -> [ Dataset ] -- Hilfsfunktion : loescht ein Vorkommen von x aus der -- Liste , falls solches vorhanden select x [] = x select x (y:yl) = if x `leq` y then select x yl else select y yl delete x [] = [] delete x (y:yl) = if (x==y) then else ©Arnd Poetzsch-Heffter TU Kaiserslautern 3. Funktionales Programmieren 341 3.2 Algorithmen auf Listen und Bäumen 342 3.2 Algorithmen auf Listen und Bäumen Sortieren durch Einfügen (insertion sort) selectionsort :: [ Dataset ] -> [ Dataset ] -- Sortieren durch Auswahl -mnm: ein minimaler Eintrag in Liste xl -rest: die Liste xl ohne min Algorithmische Idee: • Sortiere zunächst den Rest der Liste. selectionsort [] = [] selectionsort (x:xl) = let mnm = select x xl rest = delete mnm (x:xl) in mnm : ( selectionsort rest) TU Kaiserslautern TU Kaiserslautern 3. Funktionales Programmieren Sortieren durch Auswahl (selection sort) (4) ©Arnd Poetzsch-Heffter ©Arnd Poetzsch-Heffter yl y:( delete x yl) • Füge dann den ersten Eintrag in die sortierte Liste ein. 343 ©Arnd Poetzsch-Heffter TU Kaiserslautern 344 3. Funktionales Programmieren 3.2 Algorithmen auf Listen und Bäumen 3. Funktionales Programmieren 3.2 Algorithmen auf Listen und Bäumen Sortieren durch Einfügen (insertion sort) (2) Sortieren durch Einfügen (insertion sort) (3) insert :: Dataset -> [ Dataset ] -> [ Dataset ] -- Hilfsfunktion : fuegt Argument in sortierte Liste ein -- Ergebnis : sortierte Liste insertionsort :: [ Dataset ] -> [ Dataset ] -- Sortieren durch Einfuegen insert x [] = insert x (y:yl) = insertionsort [] = [] insertionsort (x:xl) = insert x ( insertionsort xl) ©Arnd Poetzsch-Heffter [x] if (x `leq` y) then x : (y:yl) else y : ( insert x yl) TU Kaiserslautern 3. Funktionales Programmieren 345 ©Arnd Poetzsch-Heffter 3.2 Algorithmen auf Listen und Bäumen TU Kaiserslautern 3. Funktionales Programmieren 3.2 Algorithmen auf Listen und Bäumen Bubblesort Bubblesort (2) Algorithmische Idee: bubble :: [ Dataset ] -> Dataset -> [ Dataset ] -> ([ Dataset ], Dataset ) -- Hilfsfunktion : liefert einen maximalen Eintrag der -- Liste und die Liste ohne den maximalen Eintrag • Schiebe einen Eintrag nach rechts heraus: I Beginne dazu mit dem ersten Eintrag x. I Wenn Schieben von x auf einen gleichen oder größeren Eintrag y stößt, schiebe y weiter. I Ergebnis: maximaler Eintrag mxe und Liste ohne mxe bubble rl x [] = (rl ,x) bubble rl x (y:yl) = if (x `leq` y) then bubble (rl ++[x]) y yl else bubble (rl ++[y]) x yl • Sortiere die Liste ohne mxe und hänge mxe an. ©Arnd Poetzsch-Heffter TU Kaiserslautern 346 347 ©Arnd Poetzsch-Heffter TU Kaiserslautern 348 3. Funktionales Programmieren 3.2 Algorithmen auf Listen und Bäumen 3. Funktionales Programmieren Bubblesort (3) 3.2 Algorithmen auf Listen und Bäumen Quicksort: Sortieren durch Teilen Algorithmische Idee: • Wähle einen beliebigen Datensatz mit Schlüssel k aus, das bubblesort :: [ Dataset ] -> [ Dataset ] -- Sortieren durch Herausschieben der maximalen -Elemente sogenannte Pivotelement. • Teile die Liste in zwei Teile: I 1. Teil enthält alle Datensätze mit Schlüsseln < k I 2. Teil enthält die Datensätze mit Schlüsseln ≥ k bubblesort [] = [] bubblesort (x:xl) = let (rl ,mxe) = bubble [] x xl in ( bubblesort rl)++[ mxe] • Wende quicksort rekursiv auf die Teillisten an. • Hänge die resultierenden Listen und das Pivotelement zusammen. ©Arnd Poetzsch-Heffter TU Kaiserslautern 3. Funktionales Programmieren 349 ©Arnd Poetzsch-Heffter 3.2 Algorithmen auf Listen und Bäumen TU Kaiserslautern 3. Funktionales Programmieren 3.2 Algorithmen auf Listen und Bäumen Quicksort: Sortieren durch Teilen (2) Quicksort: Sortieren durch Teilen (3) split :: Dataset -> [ Dataset ] -> ([ Dataset ],[ Dataset ]) -- Hilfsfkt .: teilt Liste in zwei Listen (below ,above) -- below: alle Elemente in kleiner p -- above: alle Elemente groesser gleich p qsort :: [ Dataset ] -> [ Dataset ] -- Sortieren nach der Strategie ``Teile und Herrsche ’’ split p split p let in qsort [] = [] qsort (p:rest) = let (below ,above) = split p rest in (qsort below) ++ [p] ++ (qsort above ) [] = ([] ,[]) (x:xr) = (blw ,abv) = split p xr if p `leq` x then (blw ,x:abv) else (x:blw ,abv) ©Arnd Poetzsch-Heffter TU Kaiserslautern 350 351 ©Arnd Poetzsch-Heffter TU Kaiserslautern 352 3. Funktionales Programmieren 3.2 Algorithmen auf Listen und Bäumen 3. Funktionales Programmieren Bemerkung: 3.2 Algorithmen auf Listen und Bäumen Sortieren durch Mischen: Algorithmische Idee: • Hat die Liste mehr als ein Element, berechne die Länge der Liste div 2 (halfsize). Quicksort ist ein typischer Algorithmus gemäß der Divide-and-Conquer-Strategie: • Teile die Liste in zwei Teile der Länge halfsize (+1). • Zerlege das Problem in Teilprobleme. • Sortiere die Teile. • Wende den Algorithmus auf die Teilprobleme an. • Mische die Teile zusammen. • Füge die Ergebnisse zusammen. Bemerkung: Mergesort ist auch effizient für das Sortieren von Datensätzen, die auf externen Speichermedien liegen und nicht vollständig in den Hauptspeicher geladen werden können. ©Arnd Poetzsch-Heffter TU Kaiserslautern 3. Funktionales Programmieren 353 ©Arnd Poetzsch-Heffter 3.2 Algorithmen auf Listen und Bäumen TU Kaiserslautern 3. Funktionales Programmieren 3.2 Algorithmen auf Listen und Bäumen Bemerkung: Bemerkung: (2) merge :: [ Dataset ] -> [ Dataset ] -> [ Dataset ] -- Hilfsfunktion : mischt zwei sortierte Listen -- zu einer sortierten Liste zusammen mergesort :: [ Dataset ] -> [ Dataset ] -- Sortieren durch Mischen -halfsize : Haelfte der Listenlaenge -front : Vordere Haelfte der Liste -back : Hintere Haelfte der Liste merge merge merge merge [] [] [] yl xl [] (x:xl) (y:yl) ©Arnd Poetzsch-Heffter = = = = mergesort [] = [] mergesort (x:[]) = [x] mergesort xl = let halfsize = ( length xl) `div` 2 front = take halfsize xl back = drop halfsize xl in merge ( mergesort front) ( mergesort back) [] yl xl if (x `leq` y) then x : (merge xl (y:yl)) else y : (merge (x:xl) yl) TU Kaiserslautern 354 355 ©Arnd Poetzsch-Heffter TU Kaiserslautern 356