Abstrakter Datentyp

Werbung
Algorithmen und Datenstrukturen
Werner Struckmann
Wintersemester 2005/06
4. Listen und abstrakte Datentypen
4.1 Abstrakte Datentypen
4.2 Listen
4.3 Keller
4.4 Schlangen
Datentypen
Unter einem Datentyp versteht man die Zusammenfassung von
Wertebereichen und Operationen zu einer Einheit.
◮
Abstrakter Datentyp: Schwerpunkt liegt auf den
Eigenschaften, die die Wertebereiche und Operationen
besitzen.
◮
Konkreter Datentyp: Realisierung der Wertebereiche und
Operationen stehen im Vordergrund.
◮
Primitive Datentypen: bool, char, int, real, . . .
4.1 Abstrakte Datentypen
4-1
Datentypen
Komplexe Datentypen, sog. Datenstrukturen, werden durch
Kombination primitiver Datentypen gebildet. Sie besitzen selbst
spezifische Operationen. Datenstrukturen können vorgegeben
oder selbstdefiniert sein.
Dabei wird über das Anwendungsspektrum unterschieden in
◮
Generische Datentypen: Werden für eine Gruppe ähnlicher
Problemstellungen entworfen und sind oft im Sprachumfang
bzw. der Bibliothek einer Programmiersprache enthalten
(Feld, Liste, Keller, Schlange, Verzeichnis, . . . ).
◮
Spezifische Datentypen: Dienen der Lösung einer eng
umschriebenen Problemstellung und werden im
Zusamenhang mit einem konkreten Problem definiert.
4.1 Abstrakte Datentypen
4-2
Entwurfsprinzipien für Datentypen
Anforderungen an die Definition eines Datentyps:
◮
Die Spezifikation eines Datentyps sollte unabhängig von
seiner Implementierung erfolgen. Dadurch kann die
Spezifikation für unterschiedliche Implementierungen
verwendet werden.
◮
Reduzierung der von außen sichtbaren (zugänglichen)
Aspekte auf die Schnittstelle des Datentyps. Dadurch kann
die Implementierung später verändert werden, ohne dass
Programmteile, die den Datentyp benutzen, angepasst
werden müssen.
4.1 Abstrakte Datentypen
4-3
Entwurfsprinzipien für Datentypen
Aus diesen Anforderungen heraus ergeben sich zwei Prinzipien:
◮
Kapselung (encapsulation): Alle Zugriffe geschehen immer
nur über die Schnittstelle des Datentyps.
◮
Geheimnisprinzip (programming by contract): Die interne
Realisierung des Datentyps bleibt dem Benutzer verborgen.
4.1 Abstrakte Datentypen
4-4
Abstrakte Datentypen
Informatik-Duden: Ein Datentyp, von dem nur die Spezifikation und
Eigenschaften (in Form von zum Beispiel Regeln oder
Gesetzmäßigkeiten) bekannt sind, heißt abstrakt. Man abstrahiert
hierbei von der konkreten Implementierung.
Dies kann für
◮
eine klarere Darstellung,
◮
für den Nachweis der Korrektheit oder
◮
für Komplexitätsuntersuchungen
von Vorteil sein.
Ein abstrakter Datentyp wird kurz als ADT bezeichnet.
Ein ADT wird ohne Kenntnis der internen Realisierung verwendet
(Geheimnisprinzip). Dabei wird nur von der Schnittstelle
(Kapselung) Gebrauch gemacht.
4.1 Abstrakte Datentypen
4-5
Abstrakte Datentypen
Wir werden ADTen durch algebraische Spezifikationen
beschreiben:
◮
Eine Signatur bildet die Schnittstelle eines ADTs.
◮
Mengen und Funktionen, die zur Signatur „passen“, heißen
Algebren.
◮
Axiome schränken die möglichen Algebren ein.
Der Themenkomplex „algebraische Spezifikationen“ wird hier nur
einführend behandelt.
4.1 Abstrakte Datentypen
4-6
Signaturen
Eine Signatur Σ = (S , Ω) besteht aus
◮
einer Menge von Sorten S und
◮
einer Menge von Operatorsymbolen Ω.
Jedes Operatorsymbol f : s1 . . . sn → s besteht aus einem
Namen f , einer Folge s1 , . . . , sn ∈ S , n ≥ 0, von
Argumentsorten und einer Wertesorte s ∈ S .
Operatorsymbole ohne Parameter heißen Konstante.
4.1 Abstrakte Datentypen
4-7
Algebren
Es sei eine Signatur Σ = (S , Ω) gegeben. Eine Algebra
AΣ = (AS , AΩ ) zur Signatur Σ besteht aus
◮
den Trägermengen As der Sorten s ∈ S ,
d. h. AS = {As | s ∈ S }, und
◮
(partiellen) Funktionen auf den Trägermengen
Af : As1 × . . . × Asn → As ,
d. h. AΩ = {Af | f : s1 . . . sn → s ∈ Ω}.
4.1 Abstrakte Datentypen
4-8
Beispiel
Eine Signatur für den ADT „Bool“ sei (vorläufig) gegeben durch:
S = {Bool }
Ω = {true :→ Bool ,
false :→ Bool }
Mögliche Algebren für diese Spezifikation sind:
ABool = {T , F }
Atrue ≔ T
Afalse ≔ F
erwartungskonform
ABool = N
Atrue ≔ 1
Afalse ≔ 0
große Trägermenge
ABool = {1}
Atrue ≔ 1
Afalse ≔ 1
kleine Trägermenge
4.1 Abstrakte Datentypen
4-9
Axiome
◮
Die Zahl der möglichen Algebren kann durch Axiome
eingeschränkt werden.
◮
Axiome sind (hier) Gleichungen, die die Funktionen in ihrer
Wirkung einengen.
◮
Eine Menge von Axiomen bezeichnen wir mit Φ.
4.1 Abstrakte Datentypen
4-10
Signaturdiagramme
Signaturen lassen sich übersichtlich durch Signaturdiagramme mit
Sorten als Knoten und Operatorsymbolen als Kanten darstellen:
s0
f
..
.
s
sn
Ausblick: Signaturdiagramme sind Beispiele für Graphen, die wir in
Kürze betrachten werden.
4.1 Abstrakte Datentypen
4-11
Notationen für Operatorsymbole
Mit dem Platzhaltersymbol _ für Argumente von Funktionen führen
wir die folgenden Notationen ein:
Präfix:
f (_), + + _, . . .
f (x ), + + i
Infix:
_ ≤ _, _ + _, _ ∨ _, . . .
a ≤ b , m + n, p ∨ q
Postfix:
_!, _2 , . . .
n!, x 2
Mixfix:
|_|, if _then_else _fi , . . .
|x |
Bei der Präfixnotation schreiben wir auch kurz f .
4.1 Abstrakte Datentypen
4-12
ADT der Wahrheitswerte
S = {Bool }
Ω = {true :→ Bool ,
false :→ Bool ,
¬_ : Bool → Bool ,
_ ∨ _ : Bool × Bool → Bool ,
_ ∧ _ : Bool × Bool → Bool }
Φ = {x ∧ false = false ∧ x = false ,
x ∧ true = true ∧ x = x ,
true
Bool
¬
false
∨, ∧
x ∨ true = true ∨ x = true ,
false ∨ false = false ,
¬false = true , ¬true = false }
4.1 Abstrakte Datentypen
4-13
ADT der natürlichen Zahlen
S = {Nat }
Ω = {0 :→ Nat ,
succ
0
Nat
succ : Nat → Nat }
Φ = {}
◮
◮
Damit wird z. B. die Zahl 3 als
succ (succ (succ (0))) = succ 3 (0) dargestellt.
Der Term succ n (0) stellt die natürliche Zahl n dar. Da es keine
Axiome gibt, kann dieser Term nicht vereinfacht werden.
4.1 Abstrakte Datentypen
4-14
ADT der natürlichen Zahlen
S = {Nat }
Ω = {0 :→ Nat ,
succ : Nat → Nat ,
add : Nat × Nat → Nat }
succ
0
Nat
Φ = {add (x , 0) = x ,
add (x , succ (y )) =
add
succ (add (x , y ))}
Dies ist eine formale Spezifikation der natürlichen Zahlen mit der
Konstanten 0, der Nachfolgerfunktion und der Addition.
Implementierungen sind nicht verpflichtet, die Operationen gemäß
der Axiome zu realisieren. Sie müssen lediglich das durch die
Axiome beschriebene Verhalten gewährleisten.
4.1 Abstrakte Datentypen
4-15
Beispiel
Es soll 2 + 3 berechnet werden.
2 + 3 = add (succ (succ (0)), succ (succ (succ (0))))
= succ (add (succ (succ (0)), succ (succ (0))))
= succ (succ (add (succ (succ (0)), succ (0))))
= succ (succ (succ (add (succ (succ (0)), 0))))
= succ (succ (succ (succ (succ (0)))))
Der ADT Nat erfüllt eine besondere Eigenschaft: Jeder Term
besitzt eine eindeutige Normalform succ n (0). Diese entsteht, wenn
man die Gleichungen von links nach rechts anwendet, bis alle
add-Operationssymbole verschwunden sind.
4.1 Abstrakte Datentypen
4-16
Implementierung eines abstrakten Datentyps
Implementierung eines ADTs heißt:
◮
Realisierung der Sorten s ∈ S durch Datenstrukturen As
Beispiel: Nat { B m (m-stellige Vektoren über {0, 1})
◮
Realisierung der Operatoren f : s1 . . . sn → s durch
Funktionen Af : As1 × . . . × Asn → As
Beispiel: add : Nat × Nat → Nat { _ + _ : B m × B m → B m
◮
Sicherstellen, dass die Axiome (in den Grenzen der
darstellbaren Werte bzw. der Darstellungsgenauigkeit) gelten.
4.1 Abstrakte Datentypen
4-17
Implementierung eines abstrakten Datentyps
Beispiel: ANat = B m
Darstellung von x ∈ N mit m Binärziffern zm−1 , . . . , z0 ∈ B :
x=
m
−1
X
zi · 2i
i =0
Darstellbarer Zahlenbereich: 0 ≤ x ≤ 2m − 1
Die Gültigkeit der Rechengesetze muss gewährleistet sein.
4.1 Abstrakte Datentypen
4-18
Alternative Notation
Im Folgenden wird alternativ zur mathematischen Schreibweise
folgende an Programmiersprachen angelehnte Notation genutzt:
S = {Nat }
Ω = {0 :→ Nat ,
succ : Nat → Nat ,
add : Nat × Nat → Nat }
Φ = {add (x , 0) = x ,
add (x , succ (y )) =
succ (add (x , y ))}
4.1 Abstrakte Datentypen
type Nat
import ∅
operators
0 :→ Nat
succ : Nat → Nat
add : Nat × Nat → Nat
axioms ∀i , j ∈ Nat
add (i , 0) = i
add (i , succ (j )) =
succ (add (i , j ))
4-19
Algebraische Spezifikationen
Eine Import-Anweisung erlaubt die Angabe der Sorten, die
zusätzlich zur zu definierenden Sorte benötigt werden.
Eine algebraische Spezifikation eines ADTs besteht aus
◮
einer Signatur und
◮
aus Axiomen und ggf. zusätzlich
◮
aus Import-Anweisungen.
Eine Algebra, die die Axiome erfüllt, heißt Modell der Spezifikation.
Auf die Frage nach der Existenz und Eindeutigkeit von Modellen
können wir hier nicht eingehen.
4.1 Abstrakte Datentypen
4-20
Lineare Datentypen
4.2 Listen
◮
In diesem Kapitel besprechen wir die linearen Datentypen
Liste, Keller und Schlange.
◮
Nichtlineare Datentypen sind zum Beispiel Bäume und
Graphen, die später behandelt werden.
◮
In vielen Programmiersprachen sind lineare Datentypen und
ihre grundlegenden Operationen Bestandteil der Sprache
oder in Bibliotheken verfügbar.
◮
Listen spielen in der funktionalen Programmierung eine große
Rolle.
4-21
Listen
◮
Eine (lineare) Liste ist eine Folge von Elementen eines
gegebenen Datentyps.
◮
Es können jederzeit Elemente in eine Liste eingefügt oder
Elemente aus einer Liste gelöscht werden.
◮
Der Speicherbedarf einer Liste ist daher dynamisch, d. h., er
steht nicht zur Übersetzungszeit fest, sondern kann sich noch
während der Laufzeit ändern.
◮
Listen und ihre Varianten sind die wichtigsten dynamischen
Datenstrukturen überhaupt.
x1
4.2 Listen
x2
x3
...
xn
4-22
Typische Operationen für Listen
4.2 Listen
◮
Erzeugen einer leeren Liste
◮
Testen, ob eine Liste leer ist
◮
Einfügen eines Elements am Anfang/Ende einer Liste
◮
Löschen eines Elements am Anfang/Ende einer Liste
◮
Rückgabe des ersten/letzten Elements einer Liste
◮
Bestimmen von Teillisten, zum Beispiel: Liste ohne
Anfangselement
◮
Testen, ob ein gegebener Wert in einer Liste enthalten ist
◮
Berechnen der Länge einer Liste
◮
Bestimmen des Vorgängers/Nachfolgers eines Listenelements
4-23
Parametrisierte Datentypen
4.2 Listen
◮
Die im Folgenden eingeführten abstrakten Datentypen sind
parametrisiert.
◮
Die Spezifikation eines abstrakten Datentyps kann ein oder
mehrere Sortenparameter enthalten, die unterschiedlich
instanziiert werden können.
◮
Beispiel: In der Spezifikation der Listen tritt der Parameter T
auf. List (T ) kann dann beispielsweise durch List (Bool ),
List (Nat ) oder List (List (Nat )) instanziiert werden.
4-24
Listen
[]
length
head
T
Liste
:
4.2 Listen
Nat
tail
type List (T )
import Nat
operators
[] :→ List
_ : _ : T × List → List
head : List → T
tail : List → List
length : List → Nat
axioms ∀l ∈ List , ∀x ∈ T
head (x : l ) = x
tail (x : l ) = l
length ([]) = 0
length (x : l ) = succ (length (l ))
4-25
Implementierungen
4.2 Listen
◮
Listen können mithilfe verketteter Strukturen implementiert
werden. Hier gibt es viele Varianten: einfache und doppelte
Verkettung, Zeiger auf das erste und/oder letzte Element der
Liste, zirkuläre Listen, . . .
◮
Alternativ können Listen durch Felder implementiert werden.
Die Methoden sehen dann natürlich anders aus.
Beispielsweise müssen beim Einfügen eines Elements am
Anfang der Liste alle anderen Elemente um eine Position
nach hinten verschoben werden.
◮
Dynamische Datenstrukturen nutzen den zur Verfügung
stehenden Speicherplatz weniger effizient aus. Wenn der
benötigte Speicherplatz vor dem Programmlauf genau
abgeschätzt werden kann, können statische Strukturen
sinnvoll sein.
4-26
Implementierungen
Eine Liste kann als Verkettung einzelner Objekte implementiert
werden. Man spricht von einer einfach verketteten Liste.
Beispiel: Liste von Namen („Oliver“, „Peter“, „Walter“)
tail
head
Oliver
Peter
Walter
Die Kästen stellen sogenannte Knoten (engl. node) dar. Jeder
Knoten enthält einen Wert vom Typ T und eine Referenz auf den
nächsten Knoten. Der letzte Knoten enthält den leeren Verweis.
4.2 Listen
4-27
Bestimmen des ersten Listenelements
func head(l: Liste): T begin
var k: <Referenz auf Knoten>;
k ← <Kopf der Liste l>;
if k <ungültige Referenz> then
return <keinen Wert>;
fi;
return k.wert;
end
Aufwand: Θ(1)
4.2 Listen
4-28
Einfügen eines Listenelements am Anfang
1. Erzeugen eines neuen Knotens.
2. Referenz des neuen Knotens auf den ehemaligen Kopfknoten
setzen.
3. Kopfreferenz der Liste auf den neuen Knoten setzen.
head
X
Oliver
Peter
Walter
Aufwand: Θ(1)
4.2 Listen
4-29
Einfügen eines Listenelements am Anfang
func addFirst(v: T; l: Liste): Liste begin
var k: <Referenz auf Knoten>;
k ← <neuer Knoten>;
k.wert ← v;
k.referenz ← <Kopf der Liste l>;
<Kopf der Liste l> ← k;
return l;
end
4.2 Listen
4-30
Einfügen eines Listenelements am Ende
1. Navigieren zum letzen Knoten.
2. Erzeugen eines neuen Knotens.
3. Einhängen des neuen Knotens.
head
X
Oliver
Peter
Walter
Aufwand: Θ(n)
4.2 Listen
4-31
Einfügen eines Listenelements am Ende
func addLast(v: T; l: Liste): Liste begin
var k: <Referenz auf Knoten>;
var nk: <Referenz auf Knoten>;
k ← <Kopf der Liste l>;
nk ← <neuer Knoten>;
nk.wert ← v;
nk.referenz ← <ungültige Referenz>;
if k <ungültige Referenz> then
<Kopf der Liste l> ← nk; return l;
fi;
while k.referenz <gültige Referenz> do
k ← k.referenz;
od;
k.referenz ← nk; return l;
end
4.2 Listen
4-32
Vorgänger in einfach verketteten Listen
1. Navigieren bis zum Knoten k , dabei Referenz v auf
Vorgängerknoten des aktuell betrachteten Knotens mitführen.
2. Rückgabe des Knoten v .
Aufwand: Θ(n)
Beispiel: Bestimmung des Vorgängers von „Walter“
Schritt 1 (v =⊥)
Betrachteter
Knoten: α
Schritt 2 (v = α)
Betrachteter
Knoten: β
Schritt 3 (v = β)
Betrachteter
Knoten: γ
Objekt
gefunden,
Rückgabe von
v =β
head
α
Oliver
4.2 Listen
γ
β
Peter
Walter
4-33
Vorgänger in einfach verketteten Listen
Finden des m-ten Vorgängers eines Knotens k in einer Liste:
1. Navigieren bis zum Knoten k , die Referenz v wird wie vorher
mitgeführt, beginnt aber erst am Kopf der Liste, sobald der
(m + 1)-te Knoten betrachtet wird.
2. Rückgabe von v .
Beispiel: Bestimmung des zweiten Vorgängers von „Walter“
Schritt 1 (v =⊥)
Betrachteter
Knoten: α
Schritt 2 (v =⊥)
Betrachteter
Knoten: β
Schritt 3 (v = α)
Betrachteter
Knoten: γ
Objekt
gefunden,
Rückgabe von
v =α
head
α
Oliver
4.2 Listen
γ
β
Peter
Walter
4-34
Vorgänger in doppelt verketteten Listen
Alternativ kann man auch die Datenstruktur ändern: Jeder Knoten
wird um eine Referenz auf den Vorgängerknoten ergänzt. Die
Suche nach dem m-ten Vorgänger von k erfolgt dann von k aus
nach vorne.
Schritt 2
Betrachteter
Knoten: α
Schritt 1
Betrachteter
Knoten: β
head
α
Oliver
4.2 Listen
γ
β
Peter
Walter
4-35
Doppelt verkettete Listen
◮
Der Zugriff auf den Vorgängerknoten wird vereinfacht.
◮
Es wird zusätzlicher Speicherplatz pro Element verbraucht.
◮
Verwaltung des zweiten Zeigers bedeutet zusätzlichen
Aufwand.
Beispiel: Löschen eines Knotens
head
Oliver
4.2 Listen
Peter
Walter
4-36
Keller
◮
Ein Keller (stack) ist eine Liste, auf die nur an einem Ende
zugegriffen werden kann.
◮
Keller arbeiten nach dem Last-In-First-Out-Prinzip und
werden deshalb auch LIFO-Speicher genannt.
x5
x4
x4
x3
x2
x1
4.3 Keller
4-37
Operationen für Keller
◮
Erzeugen eines leeren Kellers (empty)
◮
Testen, ob ein Keller leer ist (empty?)
◮
Rückgabe des ersten Elements eines Kellers (top)
◮
Einfügen eines Elements am Anfang eines Kellers (push)
◮
Löschen eines Elements am Anfang eines Kellers (pop)
Keller dürfen nur mithilfe dieser Operationen bearbeitet werden.
4.3 Keller
4-38
Implementierungen
◮
Realisierung durch eine Liste:
top
...
xn
◮
x1
Realisierung durch ein Feld:
top
x1 x2 x3
4.3 Keller
...
xn
4-39
Keller
empty
top
T
empty?
Stack
push
Bool
pop
Anmerkung:
pop (empty ) =⊥
top (empty ) =⊥
Diese Fälle bleiben
undefiniert.
4.3 Keller
type Stack (T )
import Bool
operators
empty :→ Stack
push : Stack × T → Stack
pop : Stack → Stack
top : Stack → T
empty ? : Stack → Bool
axioms ∀s ∈ Stack , ∀x ∈ T
pop (push (s , x )) = s
top (push (s , x )) = x
empty ?(empty ) = true
empty ?(push (s , x )) = false
4-40
Implementierungen
4.3 Keller
◮
Es ist sinnvoll, bei der Implementierung von Datenstrukturen
auf bereits vorhandene Strukturen zurückzugreifen.
◮
Der abstrakte Datentyp „Keller“ wird durch Rückgriff auf den
Datentyp „Liste“ realisiert.
4-41
Implementierungen
◮
Die Sorte Stack(T) wird implementiert durch die Menge
AList (T ) der Listen über T .
◮
Die Operatoren werden durch die folgenden Funktionen
implementiert:
empty = []
push (l , x ) = x : l
pop (x : l ) = l
top (x : l ) = x
empty ?([]) = true
empty ?(x : l ) = false
Die Fehlerfälle pop (empty ) und top (empty ) bleiben unbehandelt.
In einer konkreten Realisierung müssen hierfür natürlich
Vorkehrungen getroffen werden.
4.3 Keller
4-42
Anwendungen
Keller gehören zu den wichtigsten Datenstrukturen überhaupt. Sie
werden zum Beispiel
◮
zur Bearbeitung von Klammerstrukturen,
◮
zur Auswertung von Ausdrücken und
◮
zur Verwaltung von Rekursionen
benötigt.
4.3 Keller
4-43
Anwendungsbeispiel: Überprüfung von
Klammerstrukturen
4.3 Keller
◮
Wir werden jetzt an einem konkreten Beispiel erläutern, wie
Keller in der Praxis benutzt werden.
◮
Ziel ist es, einen Algorithmus zu entwickeln, der eine Datei
daraufhin überprüft, ob die in dieser Datei enthaltenen
Klammern (, ), [, ], { und } korrekt verwendet wurden.
Beispielsweise ist die Folge "( [a] b {c} e )" zulässig, nicht aber
"( ] ]".
4-44
Anwendungsbeispiel: Überprüfung von
Klammerstrukturen
◮
◮
Es wird ein anfangs leerer Keller erzeugt.
Es werden alle Symbole bis zum Ende der Eingabe gelesen:
◮
◮
Eine öffnende Klammer wird mit push auf den Keller
geschrieben.
Bei einer schließenden Klammer passiert folgendes:
◮
◮
◮
4.3 Keller
Fehler, falls der Keller leer ist.
Sonst wird die Operation pop durchgeführt. Fehler, falls das
Symbol, das vom Keller entfernt wurde, nicht mit der
schließenden Klammer übereinstimmt.
Alle anderen Symbole werden überlesen.
◮
Fehler, falls der Keller am Ende der Eingabe nicht leer ist.
◮
Die Eingabe ist zulässig.
4-45
Schlangen
◮
Ein Schlange (queue) ist eine Liste, bei der an einem Ende
Elemente hinzugefügt und am anderen entfernt werden
können.
◮
Schlangen arbeiten nach dem First-In-First-Out-Prinzip und
werden deshalb auch FIFO-Speicher genannt.
x5
x1
x5 x4 x3 x2 x1
4.4 Schlangen
4-46
Operationen für Schlangen
◮
Erzeugen einer leeren Schlange (empty)
◮
Testen, ob eine Schlange leer ist (empty?)
◮
Einfügen eines Elements am Ende einer Schlange (enter)
◮
Löschen eines Elements am Anfang einer Schlange (leave)
◮
Rückgabe des ersten Elements einer Schlange (front)
Schlangen dürfen nur mithilfe dieser Operationen bearbeitet
werden.
4.4 Schlangen
4-47
Implementierungen
◮
Realisierung durch eine Liste:
Ende
Anfang
...
xn
◮
Realisierung durch ein zyklisch verwaltetes Feld:
Ende
-
4.4 Schlangen
x1
-
xn ...
Anfang
x3 x2 x1
-
...
-
4-48
Schlangen
empty
front
empty?
Queue
T
enter
Bool
leave
Anmerkung:
leave (empty ) =⊥
front (empty ) =⊥
Diese Fälle bleiben
undefiniert.
4.4 Schlangen
type Queue (T )
import Bool
operators
empty :→ Queue
enter : Queue × T → Queue
leave : Queue → Queue
front : Queue → T
empty ? : Queue → Bool
axioms ∀q ∈ Queue , ∀x ∈ T
leave (enter (empty , x )) = empty
leave (enter (enter (q, x ), y )) =
enter (leave (enter (q, x )), y )
front (enter (empty , x )) = x
front (enter (enter (q, x ), y )) =
front (enter (q, x ))
empty ?(empty ) = true
empty ?(enter (q, x )) = false
4-49
Implementierungen
Der abstrakte Datentyp „Schlange“ wird ebenfalls durch den
Rückgriff auf den abstrakten Datentyp „Liste“ implementiert.
◮
Die Sorte Queue(T) wird implementiert durch die Menge
AList (T ) der Listen über T .
◮
Die Operatoren werden durch die folgenden Funktionen
implementiert:
empty = []
enter (l , x ) = x : l
leave (x : []) = []
leave (x : l ) = x : leave (l )
4.4 Schlangen
front (x : []) = x
front (x : l ) = front (l )
empty ?([]) = true
empty ?(x : l ) = false
4-50
Anwendungen
Eine häufige Anwendung sind Algorithmen zur Vergabe von
Ressourcen an Verbraucher.
◮
Prozessverwaltung:
◮
◮
◮
◮
Druckerverwaltung:
◮
◮
◮
4.4 Schlangen
Ressource: Rechenzeit
Elemente der Warteschlange: rechenwillige Prozesse
Grundidee: Jeder Prozess darf eine feste Zeit lang rechnen,
wird dann unterbrochen und hinten in die Warteschlange
wieder eingereiht, falls weiterer Bedarf an Rechenzeit
vorhanden ist.
Ressource: Drucker
Elemente der Warteschlange: Druckaufträge
Grundidee: Druckaufträge werden nach der Reihenfolge ihres
Eintreffens abgearbeitet.
4-51
Deques
4.4 Schlangen
◮
Eine deque (double-ended queue) ist eine Liste, bei der an
beiden Enden Elemente hinzugefügt und entfernt werden
können.
◮
Nach den vorangegangenen Beispielen sollte klar sein,
welche Operationen eine Deque besitzt und wie diese
implementiert werden können.
4-52
Herunterladen