Universität Bielefeld Programmieren in Haskell Giegerich Programmieren in Haskell WS 2013/2014 Robert Giegerich Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules Universität Bielefeld AG Praktische Informatik 8. Januar 2014 DatentypSpezifikationen Themen-Vorschau Universität Bielefeld Programmieren in Haskell Giegerich Pattern Matching Pattern Matching: as-Patterns und lazy Patterns Client-Server-Programmierung Abstrakte Datentypen Module in Haskell Holy Lists Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Pattern Matching in Haskell Universität Bielefeld Programmieren in Haskell Pattern matching (alias Mustervergleich) kennen wir bereits Giegerich aus Funktionsgleichungen Pattern Matching aus case-Ausdrücken Lazy patterns Ein Pattern (Muster) ist ein Ausdruck bestehend aus Variablen, der anonymen Variablen _ (“Joker”) Konstruktoren aller Datentypen Zahlen, Characters ... also keine Funktionen, die nicht Konstruktoren sind ... und keine Variable mehrfach, ausgenommen _ Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Ergonomie des Pattern Matching in Haskell Universität Bielefeld Programmieren in Haskell Giegerich Pattern matching leistet zweierlei 1 2 3 Pattern Matching Fallunterscheidung in übersichtlicher Form Lazy patterns Nutzung der Reihenfolge bei überlappenden Mustern Prüfungen, Vorbereitung Bindung von Muster-Variablen an Substrukturen des geprüften Werts Abstrakte Datentypen Haskell Modules leaves ( Leaf a ) = [ a ] Datentypleaves ( Br ( Leaf a ) y ) = a : leaves y Spezifikationen leaves ( Br ( Br x y ) z ) = leaves ( Br x ( Br y z )) As-Patterns Manchmal will man Muster verwenden und einen Namen für den (unzerlegten) Argumentwert haben Universität Bielefeld Programmieren in Haskell Giegerich 1 2 suffixes ( x : xs ) = ( x : xs ): suffixes xs erzeugt eine überflüssige (:) Operation, vergeudet Zeit und verschwendet Platz. Besser mit as-pattern suffixes z@(x:xs) = z: suffixes xs Nun ist das schon vorhandene x:xs unter dem Namen z zu haben. Nebenbei: Welche Komplexität hat die Berechnung von suffixes x? Und welche die Ausgabe des Ergebnisses? Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Pattern Matching und Auswertungreihenfolge Universität Bielefeld Programmieren in Haskell Giegerich Pattern Matching Vergleich eines Musters mit einem Wert erfordert, Lazy patterns dass der Wert ausgerechnet wird, Prüfungen, Vorbereitung aber nur soweit es für den Vergleich nötig ist (Laziness) Abstrakte Datentypen Verwendung von Mustern hat also Auswirkung auf die Berechenungsreihenfolge Haskell Modules DatentypSpezifikationen Abweisbare und nicht abweisbare Muster Universität Bielefeld Programmieren in Haskell a.k.a. refutable versus irrefutable patterns Ein Muster ohne Konstruktoren (also x, _ ) passt auf jedem Wert und heißt unabweisbar Ein Muster mit Konstruktoren heißt abweisbar 1 2 3 Ist eine Funktion mit unabweisbaren Mustern definiert, ist sie immer ein Redex. Beispiele: square x = x * x double f = f . f five _ = 5 NB: Man kann alle Funktionen auf der linken Seite mit unabweisbaren Mustern schreiben, wenn man stattdessen case-Ausdrücke auf der rechten Seite verwendet. Giegerich Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Pattern Matching und Terminierung (1) Erinnerung: Die Auswertungsstrategie in einer funktionalen Sprache hat keinen Einfluss auf den ggf. berechneten Wert, wohl aber auf die Terminierung der Berechnung. Ein Mustervergleich kann positive oder negativ ausgehen, oder divergieren Alle Muster in einer Gleichung werde top-down, links-rechts verglichen Geht ein Vergleich negativ aus, ist die aktuelle Gleichung nicht anwendbar, und es wird die nachfolgende Gleichung versucht Ruft der Vergleich eine nicht-endende Berechnung hervor, divergiert er und das Ergebnis der Gesamtrechnung ist undefiniert. Passen alle Muster einer Gleichung, wird sie angewendet Universität Bielefeld Programmieren in Haskell Giegerich Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Bottom Universität Bielefeld Programmieren in Haskell Giegerich 1 Hier eine Definition des “undefinierten” Werts > bot = bot bot, ausgesprochen “bottom” ist der gänzlich undefinierte Wert. Seine Berechnung terminiert nicht, und produziert auch keinen Konstruktor. Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules Was wäre ein teilweise undefinierter Wert? Nebenbei: Welchen Typ hat bot? DatentypSpezifikationen Pattern Matching und Terminierung (2) Universität Bielefeld Programmieren in Haskell Giegerich 1 2 3 1 2 3 Vergleiche take 0 _ = [] take _ [] = [] take n ( x : xs ) = x : take (n -1) xs und mit vertauschten Zeilen take ’ _ [] = [] take ’ 0 _ = [] take ’ n ( x : xs ) = x : take ’ (n -1) xs Einen Unterschied gibt es nur, wenn eines der Argumente undefiniert ist ... Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Unterschiedliches Terminierungsverhalten Universität Bielefeld Programmieren in Haskell Giegerich 1 2 take 0 take ’ 0 bot bot = = > [] = = > undefiniert 5 Lazy patterns Prüfungen, Vorbereitung 3 4 Pattern Matching take bot [] take ’ bot [] = = > undefiniert = = > [] Abstrakte Datentypen Haskell Modules Der Haskell Prelude implementiert die Version take. DatentypSpezifikationen Lazy Patterns Universität Bielefeld Programmieren in Haskell a.k.a. Muster mit verzögertem Vergleich Problem: Man möchte eine Funktion mit einem Muster definieren und zwar für ein Argument, das durch die Anwendung der Funktion erst berechnet wird Zwar steht fest, dass das Muster erfüllt sein wird, jedoch zum Zeitpunkt der Funktionsaufrufs muss für den Mustervergleich die gleiche Funktion wieder aufgerufen werden ... Wo kommt so etwas überhaupt vor? Giegerich Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Client-Server-Anwendungen Client-Server-Anwendungen sind parallel laufende Programme, die über Ströme kommunizieren Nachrichten an einander – Requests und Responses – werden wechselseitig erzeugt und gelesen Sie werden in “Strömen” (streams) verwaltet. Ströme sind in Haskell einfach lazy Listen, in andern Sprachen weden sie als spezieller Datentyp zur Verfügung gestellt. Universität Bielefeld Programmieren in Haskell Giegerich Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Zahlen raten als client-server Programm (1) Ratespiel: Finde eine Zahl n aus dem Intervall [0..N] durch Fragen der Art ”Ist n ≤ z?” mit geschickt gewählten Vergleichszahlen z. Rollenverteilung: Client: Erzeugt Fragen und merkt sich das Ergebnis. Seine Strategie: Intervall-Halbierung; sie führt garantiert in O(log(N)) Schritten zum Ziel. Server: Kennt n und beantwortet Fragen ”Ist n ≤ z?” mit True oder False. Der Server antwortet wahrheitsgemäß. (In einer interesssanteren Variante des Spiels darf er einmal lügen.) Kommunikation zwischen Client und Server über zwei Ströme: questions und answers. Universität Bielefeld Programmieren in Haskell Giegerich Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Zahlen raten als client-server Programm (2) Eine Frage hat die Form (l,g,u) und bedeutet ausführlich: “Ich weiß dass n ∈ [l..u] und frage ob n ≤ g.” g ist dabei die Intervallmitte, und wird, je nach Antwort, für die nächste Frage ins linke oder rechte Halbintervall verlegt. Das Ende erkennt der Client daran, dass das Intervall sich nicht mehr ändert. 1 2 3 4 5 > > > > > Universität Bielefeld Programmieren in Haskell Giegerich Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules minN = 0 DatentypSpezifikatiomaxN = 1024 nen −− t a k e d i f f [ ] = [ ] −− t a k e d i f f [ x ] = [ x ] t a k e d i f f ( x : y : x s ) = x : i f y == x then [ ] e l s e t a k e d i f f ( y : x s ) takediff nimmt den Präfix einer Liste bis zur ersten Wiederholung Zahlen raten als client-server Programm (2) Universität Bielefeld Programmieren in Haskell Wir definieren die Prozesse ask und reply, die über die Listen questions und answers kommunizieren. Giegerich Pattern Matching 1 2 3 4 patterns > guess n = t a k e d i f f (map ( \ ( _ , g , _) −> g ) q u e s t i o n s )Lazywhere Prüfungen, > answers = reply questions Vorbereitung > r e p l y ( ( _ , g , _ ) : q s ) = ( n <= g ) : r e p l y q s Abstrakte Datentypen 5 6 7 8 9 10 > > > > > q u e s t i o n s = a s k i n i t i a l a n s w e r s where Haskell Modules i n i t i a l = ( minN , (maxN + minN ) ‘ d i v ‘ 2 , maxN) a s k ( l , g , u ) ( a : a s ) = ( l , g , u ) : a s k n e w g u e s s a s where Datentypnewguess = i f a then ( l , (l + g ) ‘ d i vSpezifikatio‘ 2 , g) nen e l s e ( g +1, ( g+1 + u ) ‘ d i v ‘ 2 , u ) Diese Version funktioniert nicht – warum? Verklemmung! Universität Bielefeld Programmieren in Haskell Der Aufruf von ask (l,g,u) (a:as) Giegerich stellt eine Frage ((l,g,u)), Pattern Matching die von reply beantwortet wird (a) Lazy patterns fragt jedoch in seinem rechten Muster bereits nach dieser Antwort ((a:as)), um aus ihr die nächste Frage (newguess) zu generieren Das pattern matching beim Aufruf von ask kommt zu frueh: es verlangt ein Ergbnis von reply, bevor der gegebene Aufruf von ask das erste Element von questions generiert hat. reply seinerseits verlangt ein Ergebnis von ask ... Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Zahlen raten als client-server Programm (3) Vermeidung des übereifrigen Musters: 1 2 3 Universität Bielefeld Programmieren in Haskell Giegerich > > answers = reply questions r e p l y ( (_, g ,_ ) : qs ) = ( n <= g ) : r e p l y q s > > > > > > q u e s t i o n s = a s k i n i t i a l a n s w e r s where Prüfungen, i n i t i a l = ( minN , (maxN + minN ) ‘ d i v ‘ 2 , maxN) Vorbereitung a s k ( l , g , u ) a s = ( l , g , u ) : a s k n e w g u e s s ( t a i l a s ) where Abstrakte a = head a s Datentypen newguess = i f a then ( l , (l + g ) ‘ d i v ‘ 2 ,Haskell g) e l s e ( g +1, ( g+1 + u ) ‘ d i v ‘ 2 ,Modules u) Pattern Matching 4 5 6 7 8 9 10 Lazy patterns ask kann nun starten, ohne zuerst nach der Zerlegung der answers-Liste as zu fragen. Also kann die Frage ausgegeben und daraus die erste Antwort erzeugt werden, bevor mit head as nach ihr gefragt wird DatentypSpezifikationen Zahlen raten als client-server Programm (4) Der gleiche Effekt mit lazy pattern ~(a:as) O t h e r w i s e , t h e same a s s o l u t i o n ( 1 ) 1 Universität Bielefeld Programmieren in Haskell Giegerich 2 3 > guess ’ ’ n = t a k e d i f f (map ( \ ( _ , g , _) −> g ) q u e s t i o n s )Pattern where Matching 4 5 6 answers = reply questions r e p l y ( ( _ , g , _ ) : q s ) = ( n <= g ) : r e p l y q s > > > > > Abstrakte q u e s t i o n s = a s k i n i t i a l a n s w e r s where Datentypen i n i t i a l = ( minN , (maxN + minN ) ‘ d i v ‘ 2 , maxN) Haskell a s k ( l , g , u ) ~( a : a s ) = ( l , g , u ) : a s k n e w g u e s s a s where Modules newguess = i f a then ( l , (l + g ) ‘ d i vDatentyp‘ 2 , g) e l s e ( g +1, ( g+1 + u ) ‘ d i vSpezifikatio‘ 2 , u) 7 8 9 10 11 12 Lazy patterns > > Prüfungen, Vorbereitung nen der lazy pattern ~(a:as) wird beim Aufruf als erfüllt angenommen erst wenn a oder as gebraucht wird, findet der Abgleich statt. Lazy Patterns Universität Bielefeld Programmieren in Haskell Giegerich Lazy patterns, ~(...) sind unabweisbar. Ihre Überprüfung findet nur statt, wenn eine Komponente daraus gebraucht wird Scheitert sie, bricht die Rechnung ab. Eine weitere Mustergleichung nach einer Gleichung mit einem lazy Pattern macht also keinen Sinn. Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Top level pattern binding: immer lazy Universität Bielefeld Programmieren Werden Muster nicht auf Argumentposition, sondern in der in Haskell Definition von Konstanten benutzt, sind sie automatisch lazy: Giegerich fibp@(1:tfibp) = 1:1:[a+b | (a,b) <- zip fibp tfibp ] Hier haben wir einen as-Pattern, der eine konstante, unendliche Liste definiert, ... ... die Liste der Fibonacci-Zahlen. Das Muster bindet die Gesamtliste an den Namen fibp, zugleich ihren tail an den Namen tfibp die linke Seite fragt, ob die Liste nicht leer ist, aber dank der laziness des Musters wird dies erst geprüft, wenn tfibp rechts benutzt wird Vergleiche: let a@(1:as) = 1:[] in 2:a let a@(0:as) = 1:[] in 2:a Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Infos und Tipps zu den Prüfungen Universität Bielefeld Programmieren in Haskell Die Veranstaltungen des Moduls “Algorithmen und Datenstrukturen” werden gemeinsam in einer Modulabschluss-Prüfung geprüft. wissenschaftlicher Sinn einer Prüfung: Feststellen, ob ausreichende Grundlagen für weiterführende Veranstaltungen vorhanden sind. wissenschaftlich unsinnig, aber systemimmanent: Leistungsvergleich, Noten, peinliche Buchführung über den Ausbildungsgang zum Ausweis von Unterschieden Voraussetzung der Zulassung zur Prüfung sind 50% der Punkte aus den Übungen zu A&D+Haskell. Giegerich Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Mündliche Prüfung zum Modul A&D Welche Veranstaltungen werden geprüft? A&D Vorlesung Programmieren in Haskell A&D + Haskell Übung Programmieren in der UNIX-Umgebung Wer prüft? Prüfer Beisitzer Wie lange? 20 Minuten Wann? siehe Termine Was muss mitgebracht werden? Personalausweis bzw. Pass Studentenausweis Universität Bielefeld Programmieren in Haskell Giegerich Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Prüfungsziel Universität Bielefeld Programmieren in Haskell Giegerich Test, inwieweit Veranstaltungsinhalte verstanden werden und angewendet werden können. Pattern Matching Lazy patterns Verschiedene Arten von Leistungen Prüfungen, Vorbereitung Reproduktion Abstrakte Datentypen Transfer (konkret) Haskell Modules Transfer (abstrakt) Prüfen auf eine möglichst gute Note hin. DatentypSpezifikationen Termine Universität Bielefeld Programmieren in Haskell Robert Giegerich 2. und 4. Februar-Woche, und ggf. später Alex Sczyrba 3. Februar-Woche, 3. März-Woche Terminvereinbarung bei Stefanie Lanfermann (M3-127) (Frau Lanfermann meldet die Prüfung im Dekanat an) weitere Prüfer: Prof. Dr. Stoye (mail an Heike Samuel) Prof. Dr. Hammer (14.,17.,18.2.) (mail an Gisela Weitekemper) Prof. Dr. Nattkemper (mail an Heike Samuel) Dr. Thies Pfeiffer (mail an Stefanie Lanfermann) Giegerich Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Vorbereitung Universität Bielefeld Programmieren in Haskell Giegerich Materialien Vorlesungsfolien Übungsunterlagen Skript Prüfungsprotokolle, dabei jeweils beachten: Motivation Hintergrund Übungsgruppen Sprechstunden Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Ablauf Universität Bielefeld Programmieren in Haskell Giegerich Ausweiskontrolle Prüfer stellt die Fragen Beisitzer protkolliert hauptsächlich Einstiegsfrage nach ∼ 20 Minuten: Ende Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Student geht vor die Tür Haskell Modules Prüfer und Beisitzer beraten sich kurz DatentypSpezifikationen Student wird wieder hereingebeten Mitteilung der Note und Feedback 10 einfache Regeln zum Versemmeln einer Prüfung (G.Sauthoff) 1 2 3 4 5 6 7 8 9 10 erst 3 Tage vor dem Prüfungstermin mit der Vorbereitung beginnen alleine Lernen, obwohl das eigentlich noch nie gut geklappt hat bei der Vorbereitung das Skript ignorieren Verzicht auf eine aktive Bearbeitung der Übungen strikt vermeiden, ein Haskell-Programm selbst zu schreiben Inhalte auswendig lernen möglichst unausgeschlafen in die Prüfung kommen auf die Frage nach Unklarheiten vor dem Beginn der Prüfung am besten sehr lang und breit erklären, wie schlecht vorbereitet man doch ist kommunizieren, dass man die Begriffe in den Veranstaltungstiteln nicht erklären kann trotz Krankheit zur Prüfung erscheinen Universität Bielefeld Programmieren in Haskell Giegerich Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Weitere Angebote Universität Bielefeld Programmieren in Haskell Giegerich Pattern Matching Lazy patterns ZSB bietet Workshops an eher allgemeine Hinweise vieles ist wahrscheinlich schon bekannt eher sinnvoll bei extremer Prüfungsangst o.Ä. Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Abstrakte Datentypen Universität Bielefeld Programmieren in Haskell Giegerich In der Software-Entwicklung unterscheidet zwei Arten von Datentypen: Pattern Matching Lazy patterns konkrete Datentypen beziehen sich auf eine konkrete Repräsentation in der verwendeten Programmiersprache. Beispiele: Int, Char, Listen, Bäume; auch polymorphe Datentypen sind konkret! abstrakte Datentypen sind nicht an eine konkrete Repräsentation gebunden. Wie kann man dann überhaupt etwas definieren? Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Abstrakte Datentypen Universität Bielefeld Programmieren in Haskell Giegerich Ein abstrakter Datentyp (ADT) ist, unabhängig von einer Programmiersprache, eine (nicht weiter spezifizierte) Wertemenge mit Operationen auf dieser Wertemenge, die bestimmte Axiome (Gleichungen) erfüllen die Menge der Operationen ist die Schnittstelle (API) nach außen konkrete Repräsentation der Daten in der Implementierung ist nicht nach außen sichtbar (Kapselung) Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Warum? Universität Bielefeld Programmieren in Haskell Programmieren mit ADTs erhöht die Wiederverwendbarkeit von Programmen Beispiel: ADT Verzeichnis Giegerich Pattern Matching Lazy patterns Operationen auf Verzeichnissen: Prüfungen, Vorbereitung Erzeugen, Einfuegen, Loeschen, Sortieren, Bereinigen Programmierer, die Verzeichnisse verwenden, kennen NUR diese Operationen Verzeichnisse können konkret Listen, Bäume, Arrays,... sein, ggf. mit Zusatzoperationen Die konkrete Implementierung kann geändert werden, ohne dass andere Programme davon betroffen sind Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Abstrakte Datentypen Universität Bielefeld Programmieren in Haskell Abstrakte Datentypen spielen eine bedeutende Rolle in der Software-Entwicklung Alle modernen Programmiersprachen unterstützen ihre Verwendung Sie dienen der Spezifikation der nach außen sichtbaren Operationen und Eigenschaften Manchmal werden die Operationen mit Effizienzvorgaben verknüpft In Haskell werden abstrakte Datentypen durch Module und durch Typklassen unterstützt. In Java/C++ geschieht dies durch Klassen ud Interfaces Giegerich Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Mini-ADT Llist Universität Bielefeld Programmieren in Haskell Wir spezifizieren einen ADT Llist a analog zu Listen, dessen length-Funktion die Komplexität O(1) haben soll. Operationen: Giegerich Pattern Matching Lazy patterns empty : → Llist a in O(1) (1) Prüfungen, Vorbereitung lcons : a × Llista → Llist a in O(1) (2) Abstrakte Datentypen lhead : Llist a → a in O(1) (3) Haskell Modules Llist a → Llist a in O(1) (4) Llist a → Int in O(1) (5) in O(n) (6) ltail : llength : lapp : Llist a × Llist a → Llist a DatentypSpezifikationen Eigenschaften Universität Bielefeld Programmieren in Haskell Eigenschaften der Operationen: lhead(lcons(x , xs)) = x ltail(lcons(x , xs)) = xs llength(lcons(x , empty )) = 1 Giegerich (7) (8) (9) llength(lapp(xs, ys)) = llength(xs) + llength(ys)(10) lapp(x , empty ) = x und so weiter ... Können wir das spontan implementieren? (11) Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Module in Haskell Universität Bielefeld Programmieren in Haskell Giegerich Aus einigen Beispielen bekannt: Modulname Imports-Deklarationen Definitionen des Moduls 1 2 3 4 5 > module Meinmodul > where > import Data . List Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen > splits n = [ splitAt k [1.. n ] | k <- [1.. n -1] ] Modulaufbau — Was bisher verschwiegen wurde Universität Bielefeld Programmieren in Haskell Giegerich Ein Haskell-Modul ist eine Datei, welche wie folgt eingeleitet wird: module <Name> (<Exportliste>) where Nur die Datentypen und Funktionen, die in <Exportliste> angegeben werden, sind nach außen hin sichtbar Wenn <Exportliste> weggelassen wird, sind alle Definitionen nach außen sichtbar. Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Exportieren von Datentypen 1 module Liste ( List ) where Universität Bielefeld Programmieren in Haskell Giegerich 2 3 4 data List a = Nil | Cons a ( List a ) Pattern Matching Dies exportiert nur den Datentyp List, nicht aber die Konstruktoren Nil und Cons. Diese können in dem importierenden Modul nicht verwendet werden Prüfungen, Vorbereitung module Liste ( List ( Nil , Cons )) where exportiert auch die Konstruktoren alternativ: module Liste ( List (..)) where exportiert alle Konstruktoren eines Datentyps Lazy patterns Abstrakte Datentypen Haskell Modules DatentypSpezifikationen ADT-Beispiel: Holy Lists Universität Bielefeld Programmieren in Haskell Giegerich Anhängen eines Elements am Ende einer Liste erfordert O(n) Schritte. “Holy Lists” erlauben diese Operation in O(1). Pattern Matching Lazy patterns Prüfungen, Vorbereitung 1 2 3 4 Abstrakte Spezifikation: Datentypen > module Hlist ( l2h , h2l , lcons , rcons ) where Haskell > l2h :: [a] -> Hlist a -- in O ( n )Modules > h2l :: Hlist a -> [ a ] -- in O ( n )DatentypSpezifikatio> lcons :: a -> Hlist a -> Hlist a -- in O (1)nen > rcons :: Hlist a -> a -> Hlist a -- in O (1) Holy Lists 5 Stack Queue Set Multi-Set Holy Lists: Eigenschaften Universität Bielefeld Programmieren in Haskell Giegerich 1 2 Einige der erwarteten Eigenschaften: l2h . h2l = id ( Konvertierung ) h2l . l2h = id ( " ) Lazy patterns Prüfungen, Vorbereitung 3 4 Pattern Matching reverse ( h2l ( rcons as a )) = a : h2l as Abstrakte Datentypen Haskell Modules Implementierung ??? Zum Nachdenken über die Feiertage! Tipp: “holy” kommt von “hole” – Listen mit “Loch” am Ende ... DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Universität Bielefeld Programmieren in Haskell Giegerich Pattern Matching Schöne Feiertage und Guten Rutsch Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules Dieser Foliensatz wird noch weiter ergänzt im neuen Jahr. DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set ADT Holy Lists Universität Bielefeld Programmieren in Haskell Giegerich 1 2 3 4 Pattern type Hlist a Matching empty :: Hlist a -- klassisch Lazy patterns cons :: a -> Hlist a -> Hlist a Prüfungen, Vorbereitung append :: Hlist a -> Hlist a -> Hlist a Abstrakte Datentypen 5 6 hcons :: Hlist a -> a -> Hlist a -- neu !! l2h h2l :: [ a ] -> Hlist a :: Hlist a -> [ a ] -- Konversion Spezifikatio- Haskell Modules 7 8 9 Datentypnen Holy Lists Stack Queue Set Multi-Set Axiome für ADT Holy Lists Universität Bielefeld Programmieren in Haskell Giegerich Forderungen an die Implementierung: Alle Eigenschaften von (:), [], (++) sollen auch für cons, empty, append gelten. h2l . l2h == id hcons h a == l2h (h2l h ++ [a]) Konvertierung (l2h l) und (h2l h) in O(n) hcons h a in O(1) Die letzte Anforderung ist die eigentliche Herausforderung! Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Lösungsvorschlag Universität Bielefeld Programmieren in Haskell Giegerich 1 2 Implementierung siehe Datei hlist.lhs Pattern Auszug: Matching Hlist - Listen mit effizientem Anfuegen vorneLazyund h patterns Append - Funktion ebenfalls in O (1) Prüfungen, Vorbereitung 3 4 5 6 7 8 9 Specification Abstrakte Datentypen Haskell Modules > module Hlist ( l2h , h2l , lcons , rcons ) where > l2h :: [a] -> Hlist a -- in O ( n )DatentypSpezifikatio> h2l :: Hlist a -> [ a ] -- in O ( n )nen > lcons :: a -> Hlist a -> Hlist a -- in O (1) uns so weiter ... Holy Lists Stack Queue Set Multi-Set ADT-Beispiel: Stack Ein Stack (“Stapel” oder auch “Keller”) ist ein Datentyp, der eine Menge von gleichartigen Elementen aufnehmen kann. Er unterstützt fünf Operationen: emptystack: Liefert einen Stack ohne Inhalt Universität Bielefeld Programmieren in Haskell Giegerich Pattern Matching Lazy patterns stackEmpty s: Fragt ob ein Stack s leer ist Prüfungen, Vorbereitung push x s: Legt ein neues Element x auf den Stack s Abstrakte Datentypen pop s: Entfernt das oberste Element vom Stack Haskell Modules top s: Liefert das oberste Element des Stacks s, ohne dieses zu entfernen DatentypSpezifikationen Holy Lists Last-In-First-Out (LIFO)-Strategie: Das letzte Element, was auf den Stack gelegt wurde, ist das erste, was wieder heruntergenommen wird. Stack Queue Set Multi-Set Stack-Schnittstelle in Haskell 1 2 module Stack ( Stack , push , pop , top , emptyStack , stackEmpty ) where 3 4 5 6 7 8 emptyStack :: stackEmpty :: push :: a -> pop :: Stack top :: Stack Stack a Stack a -> Bool Stack a -> Stack a a -> Stack a a -> a emptyStack liefert einen neuen, leeren Stack stackEmpty überprüft, ob der Stack leer ist push legt ein Element auf den Stack Universität Bielefeld Programmieren in Haskell Giegerich Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue pop entfernt das oberste Element vom Stack Set Multi-Set top liefert das oberste Element vom Stack Stack-Implementierung (1) Universität Bielefeld Programmieren 1 2 > module S t a c k ( Stack , emptyStack , stackEmpty , pop , push ,in Haskell top ) > data S t a c k a = St [ a ] Giegerich 3 4 5 > emptyStack : : Stack a > e m p t y S t a c k = St [ ] Lazy patterns 6 7 8 9 > s t a c k E m p t y : : S t a c k a −> Bool > s t a c k E m p t y ( St [ ] ) = True > s t a c k E m p t y ( St _) = False 10 11 12 Pattern Matching > push : : a −> S t a c k a −> S t a c k a > push x ( St x s ) = St ( x : x s ) Der Datentyp stack wird exportiert, aber nicht der Konstruktor St! Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists NUR mittels Emptystack und push können Stacks erzeugt werden. Stack Queue Alternative: module Stack (Stack(St), emptyStack ... oder auch module Stack (Stack(..), emptyStack ... Set Multi-Set Stack-Implementierung (2) Universität Bielefeld Programmieren in Haskell Giegerich 14 15 16 > pop : : S t a c k a > pop ( St [ ] ) > pop ( St ( x : x s ) ) −> S t a c k a = e r r o r " pop : S t a c k empty ! " = St x s 17 18 19 20 > t o p : : S t a c k a −> a > t o p ( St [ ] ) = e r r o r " t o p : S t a c k empty ! " > t o p ( St ( x : x s ) ) = x Inspektion und Abbau von Stacks Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Sicherheit ... Universität Bielefeld Programmieren in Haskell Giegerich Pattern Matching Welche Rolle spielt der Konstruktor St? unterhalb von St stehen immer nur einfache Listen Lazy patterns Prüfungen, Vorbereitung eigentlich kann man ihn ganz weglassen, oder? Abstrakte Datentypen wie z.B. in ... Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Schlichte Stacks... 1 2 Programmieren > module S t a c k 2 ( Stack , emptyStack , stackEmpty , pop , push , top ) in Haskell > type S t a c k a = [ a ] Giegerich 3 4 5 > emptyStack : : Stack a > emptyStack = [ ] 6 7 8 9 11 Lazy patterns Prüfungen, Vorbereitung > push : : a −> S t a c k a −> S t a c k a > push x xs = x : xs Haskell Modules 13 14 15 16 > pop : : S t a c k a −> S t a c k a > pop [] = e r r o r " pop : S t a c k empty ! " > pop ( x : x s ) = xs 19 20 Abstrakte Datentypen DatentypSpezifikationen Holy Lists Stack Queue Set 17 18 Pattern Matching > s t a c k E m p t y : : S t a c k a −> Bool > stackEmpty [] = True > stackEmpty _ = False 10 12 Universität Bielefeld > t o p : : S t a c k a −> a > top [] = e r r o r " t o p : S t a c k empty ! " > top ( x : xs ) = x Multi-Set Der Unterschied ... Universität Bielefeld Programmieren in Haskell Giegerich Pattern Matching Den Unterschied sieht man mit Hugs an Ausdrücken wie push ’y’ emptyStack emptyStack ++ "nonono" Auf den “schlichten” Stacks sind normale Listenoperationen ausführbar – das will man gerade nicht, auch wenn es manchmal praktisch wäre, so wie im Falle von show. Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Konvertierung Universität Bielefeld Programmieren in Haskell Giegerich Pattern Matching 1 2 Die erste Lösung ist also die bessere. Ggf. brauchen wir Konvertierungsfunktionen zwischen Listen und Stacks. > l2s :: [ a ] -> Stack a > s2l : Stack a -> [ a ] Wo würde man die implementieren? Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Konvertierung wo? Universität Bielefeld Programmieren in Haskell Giegerich 1 2 1 2 3 Im Modul Stack geht es einfach und effizient: > l2s l = St l > s2l ( St l ) = l außerhalb geht es auch recht einfach, aber weniger effizient: > l2s = foldr push emptyStack l > s2l s = if s == emptyStack then [] > else top s : s2l pop s Das liegt daran, dass wir “außen” nichts über die interne Implementierung wissen. Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Beispiel: Queue Universität Bielefeld Programmieren in Haskell Giegerich Warteschlangen realisiert der ADT Queue. Eine Queue (Schlange) arbeitet (im Gegensatz zum Stack) nach dem FIFO (First In First Out)-Prinzip → das erste Element, das einer Queue hinzugefügt wurde, ist auch das erste, das wieder entfernt wird Eine Queue stellt die Operationen enqueue, dequeue und front bereit, sowie emptyQueue und queueEmpty. Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Queue-Schnittstelle in Haskell 1 2 Universität Bielefeld module Queue ( Queue , emptyQueue , queueEmpty , enqueue , dequeue , front ) where 3 4 5 6 7 8 emptyQueue queueEmpty enqueue dequeue front :: :: :: :: :: Queue a Queue a -> a -> Queue Queue a -> Queue a -> Programmieren in Haskell Giegerich Pattern Matching Bool a -> Queue a Queue a a emptyQueue liefert eine neue Queue queueEmpty überprüft, ob eine Queue leer ist enqueue fügt einer Queue ein neues Element hinzu Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue dequeue entfernt das erste Element aus der Queue Set Multi-Set front liefert das erste Element der Queue Queue Implementierung (0) Universität Bielefeld Programmieren in Haskell Giegerich Pattern Matching Vorüberlegung: Warum kann es nicht effizient werden, Queues einfach als Listen zu implementieren? Also z.B. als data Queue a = Q [a] ? Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Queue Implementierung (1) Universität Bielefeld Programmieren in Haskell Giegerich 1 2 Moduldeklaration: Pattern Matching > module Queue ( Queue , emptyQueue , queueEmpty , > enqueue , dequeue , f r o n t ) where Lazy patterns 3 4 5 6 7 8 > > > > > emptyQueue queueEmpty enqueue dequeue front :: :: :: :: :: Queue a Queue a −> a −> Queue Queue a −> Queue a −> Bool a −> Queue a Queue a a Auch hier wird kein Konstruktor exportiert! Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Queue Implementierung (1) Universität Bielefeld Programmieren in Haskell Giegerich Pattern Matching Aufbau einer Queue: 10 > data Queue a = Q [ a ] [ a ] d e r i v i n g ( Show , Eq ) 11 12 13 14 > emptyQueue > queueEmpty (Q [ ] > queueEmpty _ = Q [] [] [ ] ) = True = False 15 16 > e n q u e u e a (Q back f r o n t ) = Q ( a : back ) f r o n t Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Queue Implementierung (3) Universität Bielefeld Programmieren in Haskell Giegerich Abbau einer Queue: Pattern Matching 17 18 19 20 > d e q u e u e (Q [ ] [ ] ) = e r r o r " d e q u e u e : queue empty ! " Lazy patterns > d e q u e u e (Q back [ ] ) = d e q u e u e (Q [ ] ( r e v e r s e back )) Prüfungen, > d e q u e u e (Q back ( a : f r o n t ) ) = Q back f r o n t Vorbereitung 21 22 23 24 > f r o n t (Q [ ] [ ] ) > f r o n t (Q back [ ] ) > f r o n t (Q back ( a : f r o n t ) ) = e r r o r " f r o n t : queue emptyAbstrakte !" Datentypen = f r o n t (Q [ ] ( r e v e r s e backHaskell )) = a Modules DatentypSpezifikationen Natürlich verwenden wir eine O(n)-Implementierung von reverse Holy Lists Stack Queue Set Multi-Set Queue: Effizienz? Universität Bielefeld Programmieren in Haskell emptyQueue liefert eine neue Queue queueEmpty überprüft, ob eine Queue leer ist enqueue fügt einer Queue ein neues Element hinzu dequeue entfernt das erste Element aus der Queue front liefert das erste Element der Queue Effizienzbetrachtung: Welchen Rechenaufwand haben die einzelnen Operationen? Giegerich Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Was ist der Aufwand, wenn n Eintraege erzeugt, gelesen und entfernt werden? Amortisierte Effizienz von O(1) für dequeue. Stack Queue Set Multi-Set Varianten Universität Bielefeld Programmieren in Haskell Was ändert sich, wenn man front und dequeue zusammenlegt als frondeq:: Queue a -> (a, Queue a) ? 1 2 3 > frondeq ( Q [] []) = error " front : empty queue ! " > frondeq ( Q back []) = frondeq ( Q [] ( reverse back )) > frondeq ( Q back ( a : front )) = (a , Q back front ) Giegerich Pattern Matching Lazy patterns Prüfungen, Vorbereitung Jedes Element wird nur einmal gelesen und nur einmal revertiert. Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Nochmals die Sinnfrage: Warum kann man ohne abstrakte Datentypen den Typ Queue nicht korrekt implementieren? Stack Queue Set Multi-Set Beispiel: Mengen Universität Bielefeld Programmieren in Haskell Giegerich Mengen sind ein recht schwieriger Datentyp. Eine Menge ist eine ungeordnete Sammlung von unterschiedlichen Elementen Ein Element kann auf Mitgliedschaft in einer Menge hin überprüft werden, kann einer Menge hinzugefügt oder aus einer Menge entfernt werden Was ist daran schwierig? Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Mengen-Schnittstelle in Haskell 1 2 module Set ( Set , emptySet , setEmpty , inSet , addSet , delSet ) where Universität Bielefeld Programmieren in Haskell Giegerich 3 4 5 6 7 8 emptySet setEmpty inSet addSet delSet :: :: :: :: :: Set Set ( Eq ( Eq ( Eq a a -> Bool a ) = > a -> Set a -> Bool a ) = > a -> Set a -> Set a a ) = > a -> Set a -> Set a emptySet erzeugt eine leere Menge setEmpty überprüft, ob die Menge leer ist inSet überprüft, ob ein Element in einer Menge enthalten ist Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set addSet fügt ein Element der Menge hinzu delSet entfernt ein Element aus der Menge Multi-Set Mengen-Implementierung Universität Bielefeld Programmieren in Haskell Giegerich Pattern Matching Lazy patterns Siehe Datei set.lhs Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Multi-Set Universität Bielefeld Programmieren in Haskell Synonyme: Multimenge Multi-Set Giegerich Pattern Matching Lazy patterns Bag Prüfungen, Vorbereitung die Elementpositionen spielen keine Rolle Abstrakte Datentypen → wie bei Set → Unterschied zu Listen Elemente können vielfach enthalten sein → Unterschied zu Set Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue → wie bei Listen Set Multi-Set Multimenge Universität Bielefeld Programmieren in Haskell ∅ die leere Multimenge, Giegerich *a+ die einelementige Multimenge, die genau ein Vorkommen von a enthält, x ] y die Vereinigung der Elemente von x und y ; das „+“ im Vereinigungszeichen deutet an, dass sich die Vorkommen in x und y akkumulieren. Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules ∅]x = x x ]∅= x x ]y = y ]x (x ] y ) ] z = x ] (y ] z) DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Multimenge Universität Bielefeld Programmieren in Haskell Giegerich 1 2 3 bag :: [ a ] -> Bag a bag [] = ∅ bag ( a : as ) = *a+ ] bag as Beobachtung Eine Liste x enthält alle Elemente von y , falls bag x = bag y . In diesem Fall heißt x Permutation von y . Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Abstrakter Datentyp Multimenge Universität Bielefeld Programmieren in Haskell 1 2 3 4 module Bag ( Bag , emptyBag , bagEmpty , inBag , addBag , delBag , appendBag , eqBag ) where import List 5 6 7 8 9 10 11 12 emptyBag bagEmpty inBag addBag delBag appendBag eqBag :: :: :: :: :: :: :: Bag a Bag a -> Bool Eq a = > a -> Bag a -> Bool Eq a = > a -> Bag a -> Bag a Eq a = > a -> Bag a -> Bag a Bag a -> Bag a -> Bag a Eq a = > Bag a -> Bag a -> Bool Giegerich Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Abstrakter Datentyp Multimenge Universität Bielefeld Programmieren in Haskell emptyBag erzeugt eine neue Multimenge bagEmpty überprüft, ob eine Multimenge leer ist inBag überprüft, ob ein Element in der Multimenge enthalten ist addBag fügt ein Element einer Multimenge hinzu delBag löscht ein Element aus einer Multimenge appendBag vereinigt zwei Multimengen Was soll man von diesen Operationen halten? Giegerich Pattern Matching Lazy patterns Prüfungen, Vorbereitung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen headBag gibt das erste Element aus der Multimenge aus Holy Lists tailBag entfernt das erste Element aus der Multimenge Queue Siehe Datei bag.lhs Stack Set Multi-Set ADT-Fazit Universität Bielefeld Programmieren in Haskell Giegerich ADTs sind wesentlich für die nachhaltige Software-Entwicklung. Einzelne Programmteile können unabhängig voneinander Pattern Matching Lazy patterns entwickelt Prüfungen, Vorbereitung verbessert und erweitert Abstrakte Datentypen ausgetauscht werden Haskell Modules und haben klare Schnittstellen (auch für das Verstehen des Programms) DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set