Abstrakte Datentypen - Universität Bielefeld

Werbung
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
Herunterladen