Programmieren in Haskell Stefan Janssen Programmieren in Haskell Abstrakte Datentypen Stefan Janssen Universität Bielefeld AG Praktische Informatik 5. Januar 2015 Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Themen-Vorschau Programmieren in Haskell Stefan Janssen Abstrakte Datentypen Abstrakte Datentypen Module in Haskell Holy Lists Haskell Modules DatentypSpezifikationen Abstrakte Datentypen Programmieren in Haskell Stefan Janssen In der Software-Entwicklung unterscheidet zwei Arten von Datentypen: 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? Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Abstrakte Datentypen Programmieren in Haskell 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) Stefan Janssen Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Warum? Programmieren in Haskell Programmieren mit ADTs erhöht die Wiederverwendbarkeit von Programmen Beispiel: ADT Verzeichnis Stefan Janssen Abstrakte Datentypen Haskell Operationen auf Verzeichnissen: Modules Erzeugen, Einfuegen, Loeschen, Sortieren, Bereinigen Datentyp- 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 Spezifikationen Abstrakte Datentypen 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 Stefan Janssen Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Mini-ADT Llist Programmieren in Haskell Wir spezifizieren einen ADT Llist a analog zu Listen, dessen length-Funktion die Komplexität O(1) haben soll. Operationen: empty : → Llist a in O(1) (1) lcons : a × Llista → Llist a in O(1) (2) lhead : Llist a → a in O(1) (3) 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 Stefan Janssen Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Eigenschaften Programmieren in Haskell Eigenschaften der Operationen: lhead(lcons(x , xs)) = x ltail(lcons(x , xs)) = xs llength(lcons(x , empty )) = 1 Stefan Janssen (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? Können wir. Siehe Tafel. (11) Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Module in Haskell Programmieren in Haskell Stefan Janssen Aus einigen Beispielen bekannt: Modulname Abstrakte Datentypen Imports-Deklarationen Haskell Modules Definitionen des Moduls DatentypSpezifikationen Module in Haskell Programmieren in Haskell Stefan Janssen Aus einigen Beispielen bekannt: 1 2 3 Modulname Abstrakte Datentypen Imports-Deklarationen Haskell Modules Definitionen des Moduls DatentypSpezifikationen > module Meinmodul > where > import Data . List 4 5 > splits n = [ splitAt k [1.. n ] | k <- [1.. n -1] ] Modulaufbau — Was bisher verschwiegen wurde Programmieren in Haskell Stefan Janssen 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. Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Exportieren von Datentypen 1 module Liste ( List ) where 2 3 4 data List a = Nil | Cons a ( List a ) Dies exportiert nur den Datentyp List, nicht aber die Konstruktoren Nil und Cons. Diese können in dem importierenden Modul nicht verwendet werden module Liste ( List ( Nil , Cons )) where exportiert auch die Konstruktoren alternativ: module Liste ( List (..)) where exportiert alle Konstruktoren eines Datentyps Programmieren in Haskell Stefan Janssen Abstrakte Datentypen Haskell Modules DatentypSpezifikationen ADT-Beispiel: Holy Lists Programmieren in Haskell Anhängen eines Elements am Ende einer Liste erfordert O(n) Schritte. “Holy Lists” erlauben diese Operation in O(1). Stefan Janssen Abstrakte Datentypen Haskell Modules Datentyp- 1 SpezifikatioSpezifikation: nen > module Hlist ( Hlist , l2h , h2l , lcons , rcons , happ ) whe > l2h :: [a] -> Hlist a -- in O ( n ) > h2l :: Hlist a -> [ a ] -- in O ( n ) > lcons :: a -> Hlist a -> Hlist a -- in O (1) > rcons :: Hlist a -> a -> Hlist a -- in O (1) Holy Lists Stack 2 3 4 5 Queue Set Multi-Set Holy Lists: Eigenschaften Programmieren in Haskell Stefan Janssen Einige der erwarteten Eigenschaften: Abstrakte Datentypen 1 2 3 l2h . h2l = id h2l . l2h = id ( Konvertierung ) ( " ) Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Holy Lists: Eigenschaften Programmieren in Haskell Stefan Janssen Einige der erwarteten Eigenschaften: Abstrakte Datentypen 1 2 3 l2h . h2l = id h2l . l2h = id ( Konvertierung ) ( " ) Haskell Modules DatentypSpezifikationen Holy Lists Implementierung ??? Zum Nachdenken über die Feiertage! Tipp: “holy” kommt von “hole” – Listen mit “Loch” am Ende ... Stack Queue Set Multi-Set Programmieren in Haskell Stefan Janssen Schöne Feiertage und Guten Rutsch Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue PS: Purer Zufall, dass die HolyLists immer in die letzte Vorlesung vor Weihnachten fallen ... Set Multi-Set ADT Holy Lists Programmieren in Haskell Stefan Janssen 1 2 3 4 type Hlist a Abstrakte Datentypen empty :: Hlist a -- klassisch Haskell cons :: a -> Hlist a -> Hlist a Modules append :: Hlist a -> Hlist a -> Hlist a DatentypSpezifikationen 5 6 hcons :: Hlist a -> a -> Hlist a -- neu !! Holy Lists Stack Queue 7 Set 8 9 l2h h2l :: [ a ] -> Hlist a :: Hlist a -> [ a ] -- Konversion Multi-Set Axiome für ADT Holy Lists Programmieren in Haskell Stefan Janssen 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) Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set hcons h a in O(1) Die letzte Anforderung ist die eigentliche Herausforderung! Multi-Set Lösungsvorschlag Programmieren in Haskell Stefan 1 2 Implementierung siehe Datei Hlist.lhs Janssen Auszug: Abstrakte Hlist - Listen mit effizientem Anfuegen vorneDatentypen und h Haskell Append - Funktion ebenfalls in O (1) Modules 3 4 5 Specification DatentypSpezifikationen Holy Lists Stack 6 > module Hlist ( Hlist , l2h , h2l , lcons , rcons , happ ) whe > l2h :: [a] -> Hlist a -- in O ( n ) > h2l :: Hlist a -> [ a ] -- in O ( n ) > lcons :: a -> Hlist a -> Hlist a -- in O (1) uns so weiter ... Queue Set 7 8 9 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 stackEmpty s: Fragt ob ein Stack s leer ist push x s: Legt ein neues Element x auf den Stack s pop s: Entfernt das oberste Element vom Stack Programmieren in Haskell Stefan Janssen Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue top s: Liefert das oberste Element des Stacks s, ohne dieses zu entfernen Last-In-First-Out (LIFO)-Strategie: Das letzte Element, was auf den Stack gelegt wurde, ist das erste, was wieder heruntergenommen wird. Set Multi-Set Stack-Schnittstelle in Haskell 1 2 module Stack ( Stack , push , pop , top , emptyStack , stackEmpty ) where Programmieren in Haskell Stefan Janssen 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 Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack emptyStack liefert einen neuen, leeren Stack stackEmpty überprüft, ob der Stack leer ist push legt ein Element auf den Stack pop entfernt das oberste Element vom Stack top liefert das oberste Element vom Stack Queue Set Multi-Set Stack-Implementierung (1) 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 ] Stefan Janssen 3 4 5 > emptyStack : : Stack a > e m p t y S t a c k = St [ ] 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 Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists > push : : a −> S t a c k a −> S t a c k a > push x ( St x s ) = St ( x : x s ) Stack Queue Set Multi-Set Stack-Implementierung (1) 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 ] Stefan Janssen 3 4 5 > emptyStack : : Stack a > e m p t y S t a c k = St [ ] 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 Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists > push : : a −> S t a c k a −> S t a c k a > push x ( St x s ) = St ( x : x s ) Stack Queue Set Multi-Set Der Datentyp stack wird exportiert, aber nicht der Konstruktor St! NUR mittels Emptystack und push können Stacks erzeugt werden. Alternative: module Stack (Stack(St), emptyStack ... oder auch module Stack (Stack(..), emptyStack ... Stack-Implementierung (2) Programmieren in Haskell Stefan Janssen 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 Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Inspektion und Abbau von Stacks Set Multi-Set Sicherheit ... Programmieren in Haskell Stefan Janssen Abstrakte Datentypen Welche Rolle spielt der Konstruktor St? unterhalb von St stehen immer nur einfache Listen eigentlich kann man ihn ganz weglassen, oder? Haskell Modules DatentypSpezifikationen Holy Lists wie z.B. in ... 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 ] Stefan Janssen 3 4 5 > emptyStack : : Stack a > emptyStack = [ ] 6 7 8 9 > s t a c k E m p t y : : S t a c k a −> Bool > stackEmpty [] = True > stackEmpty _ = False 10 11 12 > push : : a −> S t a c k a −> S t a c k a > push x xs = x : xs 15 16 19 20 DatentypSpezifikationen Holy Lists Stack Queue Multi-Set > 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 17 18 Haskell Modules Set 13 14 Abstrakte Datentypen > 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 Der Unterschied ... Programmieren in Haskell Stefan Janssen Den Unterschied sieht man mit Hugs an Ausdrücken wie push ’y’ emptyStack emptyStack ++ "nonono" Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Der Unterschied ... Programmieren in Haskell Stefan Janssen 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. Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Konvertierung Programmieren in Haskell Stefan Janssen Abstrakte Datentypen 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? Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Konvertierung wo? Programmieren in Haskell Stefan Janssen 1 2 Im Modul Stack geht es einfach und effizient: > l2s l = St l > s2l ( St l ) = l Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Konvertierung wo? Programmieren in Haskell Stefan Janssen 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 > 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. Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Beispiel: Queue Programmieren in Haskell Stefan Janssen 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. Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Queue-Schnittstelle in Haskell 1 2 module Queue ( Queue , emptyQueue , queueEmpty , enqueue , dequeue , front ) where Programmieren in Haskell Stefan Janssen 3 4 5 6 7 8 emptyQueue queueEmpty enqueue dequeue front :: :: :: :: :: Queue a Queue a -> a -> Queue Queue a -> Queue a -> Abstrakte Datentypen Bool a -> Queue a Queue a a Haskell Modules DatentypSpezifikationen Holy Lists Stack 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 Queue Set Multi-Set Queue Implementierung (0) Programmieren in Haskell Stefan Janssen Abstrakte Datentypen Vorüberlegung: Warum kann es nicht effizient werden, Queues einfach als Listen zu implementieren? Also z.B. als data Queue a = Q [a] ? Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Queue Implementierung (1) Programmieren in Haskell Stefan Janssen Moduldeklaration: 1 2 > module Queue ( Queue , emptyQueue , queueEmpty , > enqueue , dequeue , f r o n t ) where Abstrakte Datentypen Haskell Modules 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! DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Queue Implementierung (1) Programmieren in Haskell Stefan Janssen Abstrakte Datentypen Aufbau einer Queue: 10 > data Queue a = Q [ a ] [ a ] d e r i v i n g ( Show , Eq ) Haskell Modules > emptyQueue > queueEmpty (Q [ ] > queueEmpty _ DatentypSpezifikationen 11 12 13 14 = Q [] [] [ ] ) = True = False 15 16 Holy Lists Stack Queue > e n q u e u e a (Q back f r o n t ) = Q ( a : back ) f r o n t Set Multi-Set Queue Implementierung (3) Programmieren in Haskell Stefan Janssen Abbau einer Queue: 17 18 19 20 Abstrakte > d e q u e u e (Q [ ] [ ] ) = e r r o r " d e q u e u e : queue empty !" Datentypen > 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 )) Haskell Modules > d e q u e u e (Q back ( a : f r o n t ) ) = Q back f r o n t Datentyp- 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 emptySpezifikatio!" nen = f r o n t (Q [ ] ( r e v e r s e backHoly ) )Lists Stack = a Queue Set Multi-Set Natürlich verwenden wir eine O(n)-Implementierung von reverse Queue: Effizienz? Programmieren in Haskell emptyQueue liefert eine neue Queue Stefan Janssen queueEmpty überprüft, ob eine Queue leer ist enqueue fügt einer Queue ein neues Element hinzu Abstrakte Datentypen dequeue entfernt das erste Element aus der Queue Haskell Modules front liefert das erste Element der Queue DatentypSpezifikationen Holy Lists Effizienzbetrachtung: Welchen Rechenaufwand haben die einzelnen Operationen? Was ist der Aufwand, wenn n Eintraege erzeugt, gelesen und entfernt werden? Stack Queue Set Multi-Set Queue: Effizienz? Programmieren in Haskell emptyQueue liefert eine neue Queue Stefan Janssen queueEmpty überprüft, ob eine Queue leer ist enqueue fügt einer Queue ein neues Element hinzu Abstrakte Datentypen dequeue entfernt das erste Element aus der Queue Haskell Modules front liefert das erste Element der Queue DatentypSpezifikationen Holy Lists Effizienzbetrachtung: Welchen Rechenaufwand haben die einzelnen Operationen? 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 Programmieren in Haskell Was ändert sich, wenn man front und dequeue zusammenlegt als frondeq:: Queue a -> (a, Queue a) ? Stefan Janssen Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Varianten 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 ) Stefan Janssen Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Varianten 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 ) Jedes Element wird nur einmal gelesen und nur einmal revertiert. Stefan Janssen Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Varianten 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 ) Jedes Element wird nur einmal gelesen und nur einmal revertiert. Stefan Janssen Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Nochmals die Sinnfrage: Warum kann man ohne abstrakte Datentypen den Typ Queue nicht korrekt implementieren? Beispiel: Mengen Programmieren in Haskell Stefan Janssen 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? 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 Programmieren in Haskell emptySet setEmpty inSet addSet delSet Abstrakte Datentypen Stefan Janssen 3 4 5 6 7 8 :: :: :: :: :: 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 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 Programmieren in Haskell emptySet setEmpty inSet addSet delSet Abstrakte Datentypen Stefan Janssen 3 4 5 6 7 8 :: :: :: :: :: 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 Haskell Modules DatentypSpezifikationen Holy Lists emptySet erzeugt eine leere Menge Stack Queue Set setEmpty überprüft, ob die Menge leer ist inSet überprüft, ob ein Element in einer Menge enthalten ist addSet fügt ein Element der Menge hinzu delSet entfernt ein Element aus der Menge Multi-Set Mengen-Implementierung Programmieren in Haskell Stefan Janssen Abstrakte Datentypen Haskell Modules Siehe Datei set.lhs DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Multi-Set Programmieren in Haskell Synonyme: Multimenge Multi-Set Bag Stefan Janssen Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Multi-Set Programmieren in Haskell Synonyme: Multimenge Multi-Set Stefan Janssen Abstrakte Datentypen Bag Haskell Modules die Elementpositionen spielen keine Rolle DatentypSpezifikationen → wie bei Set Holy Lists → Unterschied zu Listen Queue Stack Set Multi-Set Elemente können vielfach enthalten sein → Unterschied zu Set → wie bei Listen Multimenge Programmieren in Haskell ∅ die leere Multimenge, Stefan Janssen *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. Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack ∅]x = x x ]∅= x x ]y = y ]x (x ] y ) ] z = x ] (y ] z) Queue Set Multi-Set Multimenge Programmieren in Haskell Stefan Janssen 1 2 3 bag :: [ a ] -> Bag a bag [] = ∅ bag ( a : as ) = *a+ ] bag as Beobachtung Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Eine Liste x enthält alle Elemente von y , falls bag x = bag y . In diesem Fall heißt x Permutation von y . Stack Queue Set Multi-Set Abstrakter Datentyp Multimenge 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 Stefan Janssen Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Abstrakter Datentyp Multimenge 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 Stefan Janssen Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set Abstrakter Datentyp Multimenge 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 Stefan Janssen Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set Was soll man von diesen Operationen halten? headBag gibt das erste Element aus der Multimenge aus tailBag entfernt das erste Element aus der Multimenge Siehe Datei bag.lhs Multi-Set ADT-Fazit Programmieren in Haskell Stefan Janssen ADTs sind wesentlich für die nachhaltige Software-Entwicklung. Einzelne Programmteile können unabhängig voneinander entwickelt verbessert und erweitert ausgetauscht werden und haben klare Schnittstellen (auch für das Verstehen des Programms) Abstrakte Datentypen Haskell Modules DatentypSpezifikationen Holy Lists Stack Queue Set Multi-Set