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