Die Präsentation wird geladen. Bitte warten

Die Präsentation wird geladen. Bitte warten

1 Kapitel 5: Von Datenstrukturen zu Abstrakten Datentypen 5.1 Zur Erinnerung: Datenstrukturen in Pascal Listenverarbeitung in Pascal 5.2 Abstrakte Datentypen.

Ähnliche Präsentationen


Präsentation zum Thema: "1 Kapitel 5: Von Datenstrukturen zu Abstrakten Datentypen 5.1 Zur Erinnerung: Datenstrukturen in Pascal Listenverarbeitung in Pascal 5.2 Abstrakte Datentypen."—  Präsentation transkript:

1 1 Kapitel 5: Von Datenstrukturen zu Abstrakten Datentypen 5.1 Zur Erinnerung: Datenstrukturen in Pascal Listenverarbeitung in Pascal 5.2 Abstrakte Datentypen und Objektorientierung Brüche als ADT Implementierung - objektorientiert Brüche als ADT Implementierung - funktional 5.3 Geordnete lineare Zusammenfassungen: Listen bzw. Folgen Cons-Zelle als Basisklasse Statisch-funktionale Listenimplementierung Listenimplementierung mit Objektmethoden 5.4 Stacks und Queues Zustandsorientierte Stack-Implementierung 5.5 Find and Merge Implementierungen Implementierung mit Bäumen 5.6 Priority Queues

2 2 Eigenschaften von Typ-Konstruktoren in Pascal: (nicht in Java) Array (Feld)Record (Verbund)Set (Menge) VereinbarungS: Array [N.. N + k-1] of T S: Record F 1 : T 1 ;... ; F n : T n end S: Set of T SelektorS[i]S[i] S.F i Zugriff Index FeldnameEnthalten-Test Kardinalität#(T) k #(T 1 )... #(T n ) 2 #(T)

3 3 Für in der Tabelle aufgeführte Datentypen: jeweils ein fester Speicherbedarf. Rekursive Datenstrukturen: Speicherbedarf kann sich ändern! Muss dynamisch zugewiesen (und freigegeben werden). Rekursive Datenstrukturen in Pascal: realisiert durch Zeigertypen

4 4 Rekursive Datenstrukturen als Zeiger-Typen in Pascal: Typdefinition: type S = ^T (Zeiger auf Elemente vom Typ T) nil : ausgezeichneter Wert, steht für eine leere (terminierende) Belegung eines beliebigen Zeigertyps. Dereferenzierung: Ist z eine Zeigervariable, so ist z^ der Inhalt der durch die Variable repräsentierten Speicheradresse. Der aktuelle Wertebereich des Typs T ist die Menge aller bisherigen Adressen von T-Variablen ergänzt um nil.

5 5 Rekursive Datenstrukturen als Zeiger-Typen in Pascal: Variablendeklaration: var p: T Initialisierung einer T-Variablen: new(p) new(p) erzeugt eine neue (unbenannte) Variable vom Typ T und stellt den benötigten Speicher bereit.

6 6 Beispiel 1: Stammbaum Grundelement: type Person = RECORD Name: String; Vater, Mutter:^Person END; Deklaration und (leere) Initialisierung: var p,q: ^Person; p := nil; q := nil; Speicher-Allokation und Instantiierung: new(p); p^.Name := "Franz"; p^.Vater := nil; p^.Mutter := nil; new(q); q^.Name := "Georg"; q^.Vater := nil; q^.Mutter := nil; p^.Vater := q; new(q); q^.Name := "Maria"; q^.Vater := nil; q^.Mutter := nil; p^.Mutter := q;

7 7 Speicherfreigabe: In Java: garbage collector. In Pascal: kein garbage collector. Stattdessen: explizit mittels dispose, z.B.: dispose(p^.Vater); dispose(p^.Mutter);

8 8 Beispiel 2: Algebraische Ausdrücke Deklaration: type TermRef = ^Term; type ExprRef = ^OpExpr; type OpExpr = RECORD Op: Operator; Opd1, Opd2: TermRef END; type Term = RECORD case Atomic: Boolean of true: (Atom: Symbol); false: (SubExpr: ExprRef) END; Wieso nicht einfach... type ExprRef = ^Expr; type Expr = RECORD Op: Operator; Opd1, Opd2: ExprRef END;

9 9 Beispiel 3: Listen-Verarbeitung in Pascal: program list1; type NString = String[20]; Pos = integer; List = ^El; El = Record Content: NString; Id: Pos; Succ: List end;

10 10 var L1,L2: List; LastPos: integer; Com: String; function isempty(L: List) : boolean; begin isempty := (L=nil) end; procedure newlist(var L: List); begin new(L); LastPos := 0; L := nil end;

11 11 procedure cons(var L: List; Name: NString); var X: List; begin new(X); X^.Content := Name; X^.Id := LastPos+1; X^.Succ := L; L := X; LastPos := LastPos+1; end; procedure lcons(var L: List; Name: NString); var X,Y: List; begin new(X); X^.Content := Name; X^.Id := LastPos+1; X^.Succ := nil; if isempty(L) then L := X else begin new(Y); Y := L; while NOT ( Y^.Succ = nil ) do Y := Y^.Succ; Y^.Succ := X end; LastPos := LastPos+1; end;

12 12 procedure delete(var L: List; Posit: Pos); var X,Y: List; begin Y := L; if isempty(Y) then (* empty *) else if isempty(Y^.Succ) AND (Y^.Id = Posit) then L := nil else begin while NOT ( ( Y^.Id = Posit ) OR ( Y^.Succ^.Succ = nil ) ) do Y := Y^.Succ; if Y^.Id = Posit then begin X:= Y^.Succ; Y^ := X^ end else if (Y^.Succ^.Succ = nil) then if Y^.Succ^.Id = Posit then begin X := Y^.Succ; Y^.Succ := nil; dispose(X) end else (* empty *) end end;

13 Abstrakte Datentypen und Objektorientierung Abstrakter Datentyp (ADT): Implementierungsunabhängige Spezifikation von Datenstrukturen. (analog zur implementierungsunabhängigen Beschreibung von Algorithmen) Zwei Methoden der ADT-Spezifikation: die algebraische und die axiomatische. Sie haben gemeinsam: die Angabe der Signatur.

14 14 Signatur legt fest: Sorten (Objektmengen), Operationen, inbesondere, was für Objekte Eingabe und Ausgabe der Operationen sind. Die Signatur definiert die Syntax und Typstruktur einer Datenstruktur.

15 15 Beispiel: Menge ganzer Zahlen (IntSet) Signatur: algebra (bzw. adt) IntSet sorts IntSet, int, boolean ops emptySet: IntSet insertEl: int x IntSet IntSet deleteEl: int x IntSet IntSet member: int x IntSet boolean isEmpty: IntSet boolean

16 16 Operationale Semantik: Algebraische Spezifikation gibt als Semantik Algebren an, also Mengen (Semantik der Sorten) mit Funktionen (Semantik der Operationen). sets IntSet = {S | S Teilmenge von Z, S endlich} functions emptySet() := {} insertEl(x,S) := {x} S deleteEl(x,S) := S \ {x} member(x,S) := true falls x in S, false sonst isEmpty(S) := ( S={} ) end IntSet.

17 17 Operationale Semantik: Axiomatische Methode spezifiziert die Semantik der Operationen über Axiome (als Postulate): axioms isEmpty(emptySet()) = true isEmpty(insertEl(x,S)) = false (für alle x, S ) insertEl(x,insertEl(x,S)) = insertEl(x,S) (dito) member(x,insertEl(x,S)) = true (dito) member(x,deleteEl(x,S)) = false (dito) insertEl(x,deleteEl(x,S)) = insertEl(x,S) (dito) member(x,insertEl(y,S)) = true (für x <> y, alle S )...

18 18 Axiomatische Methode Vorteile: Man muss nur soviel festlegen, wie nötig (gibt Freiheit bei der Implementierung). Beachte: Zu einem axiomatisch spezifizierten Datentyp kann es mehrere verschiedene Algebren geben, die alle Axiome erfüllen (polymorpher Datentyp). Präzise Sprache: ermöglicht evtl. formale Verifikation der Spezifikation. Nachteile Bei größeren Anwendungen: sehr viele Axiome. Spezifikation anhand von Axiomen oft schwer zu verstehen. Charakterisierung einer gewünschten Datenstruktur durch Axiome oft schwer (Widerspruchsfreiheit und Vollständigkeit der Axiome).

19 19 Abbildung von ADT-Spezifikationen in Programmiersprachen: Kapselung: In einer ADT-Spezifikation werden zugleich Datentypen und Operationen spezifiziert Operationen sind damit an den Typ gebunden. Daher möglich: Überladung: ein und derselbe Operator kann je nach Typ unterschiedlich implementiert sein. Diese Aspekte finden sich unmittelbar in objektorientierten Programmiersprachen.

20 20 Beispiel: Brüche als abstrakte Datentypen Algebraische Spezifikation: algebra Fract sorts Fract, int ops initFract: int x (int \ {0}) Fract normFract: Fract Fract addFract, multFract,...: Fract x Fract Fract sets Fract = {F=(z,n) | z aus Z, n aus Z \ {0}} functions initFract(x,y) := (x,y) normFract(F) :=... end Fract.

21 21 Implementierung von Brüchen in Java: Alternativen: Statisch-funktional: z.B. public static FractionB add(FractionB f1, FractionB f2) {…} Objektorientiert: z.B. public FractionA add(FractionA f2) {…} Der Bruch f1 wird hier implizit verwendet (explizit mittels this )

22 Geordnete lineare Zusammen- fassungen: Listen bzw. Folgen Listen: endliche Folgen. Unterschied zu Mengen: Es gibt eine Reihenfolge der Elemente. Ein Element kann auch mehrfach vorkommen. Nebenbei: es gibt auch noch Multisets. Bei ihnen gibt es keine Reihenfolge, aber ein Element kann mehrfach vorkommen.

23 23 Implementierung von Listen mittels: statischer Speicherstrukturen: Array Vorteil: - Zugriff auf einzelne Elemente in Zeit O(1). Nachteile: - Listengröße durch Arraygröße beschränkt. - Speicherbedarf: bedingt durch Arraygröße, nicht die tatsächliche (meistens kleinere!) Größe der Liste dynamischer Speicherstrukturen: Zeigerstrukturen. Vorteile: - Beliebig große Listen möglich, Größenänderung während des Programmablaufs kein Problem. - Speicherbedarf: nur der wirklich von der Liste benötigte Platz ( (n)). Nachteil: - Zugriff auf einzelne Elemente im Schnitt in Zeit (n).

24 24 Aufwandsvergleich für Listen als Array bzw. einfach verkettete Zeigerstruktur: Im Array vorn anfügen kostet O(n). Grund: man muss alles verschieben! OperationArrayEinfach verkettete Liste Vorn anfügenO(n)O(1) Hinten anfügenO(1)O(n) Konkatenation mit |Liste1|=k, |Liste2|=m O(m)O(k) Element-SucheO(n)

25 25 Eine Signatur für Listen algebra List sorts List, El, boolean ops emptyList: List first: List El rest (bzw. butFirst): List List cons (bzw. insertFirstEl): El x List List null (bzw. isEmpty): List Boolean Die folgenden Operationen kann auf die oben angegebenen Operationen zurückgeführt werden: member: El x List boolean concat (bzw. appendList): List x List List

26 26 Algebraische Spezifikation der Semantik: sets list = { (a 1,…,a n ) | n 0, a i aus E l } functions emptyList = nil first (a 1 …a n ) = a 1, falls n 1, undefiniert, falls n=0 rest (a 1 …a n ) = (a 2 …a n ), falls n 1, undefiniert, falls n=0 cons (b, a 1 …a n ) = (b a 1 …a n ) null (a 1 …a n ) = (n=0) concat (a 1 …a n, b 1 …b m ) = (a 1,…,a n b 1 …b m ) member (b, a 1 …a n ) = true, falls es ein i gibt mit a i =b, false sonst

27 27 Einfach verkettete Listen in Java: Zuerst: Definition der Klasse einer Cons-Zelle (bestehend aus einem Wert und einem Zeiger auf eine Cons-Zelle). class PCell { private Object elem; private PCell succ; // Konstruktor: PCell(Object c) { elem = c; succ = null; } // Selektor- und Modifikator-Methoden: public Object getEl() { return this.elem; } public void setEl(Object c) { this.elem = c; } public PCell getSucc() { return this.succ; } public void setSucc(PCell next) { this.succ = next; }

28 28 Implementation Dann: Implementation gemäß der Signatur. Das geht statisch-funktional public static LiLiS insertFirstEl(Object El, LiLiS L) { PCell h = new PCell(El); if (! isEmpty(L) ) h.setSucc(L.head); return new LiLiS(h); } objektorientiert public LiLiO insertFirstEl(Object El) { PCell h = new PCell(El); if (! this.isEmpty() ) h.setSucc(this.head); return new LiLiO(h); }

29 29 Doppelt verkettete Listen Zur effizienteren Implementierung von last und concat : doppelt verkettete Listen:

30 30 InsertFirstEL in DoLi public static DoLiS insertFirstEl(Object El, DoLiS L) { DoLiS R; DCell h,cf; R = new DoLiS(L); h = new DCell(El); cf = R.head.getSucc(); R.head.setSucc(h); h.setPred(R.head); h.setSucc(cf); if (cf!=R) cf.setPred(h); else R.head.setPred(h); return R; } h cf=a 1 R

31 Stacks und Queues Stacks (Keller) und Queues (Warteschlangen): Datenstrukturen, die zur dynamischen, reihenfolgeabhängigen Verwaltung beliebiger Elemente dienen. Stacks: LIFO-Prinzip ("last in - first out") Queues: FIFO-Prinzip ("first in - first out") Beide: Spezialfälle von Listen

32 32 Stacks: LIFO Die Grundoperationen auf einem Stack entsprechen den bereits definierten Listenoperationen: top first push insertFirstEl pop butFirst

33 33 Stacks: axiomatische ADT-Spezifikation: adt Stack sorts Stack, El, boolean ops emptyStack: Stack top: Stack El pop: Stack Stack push: El x Stack Stack isEmpty: Stack Boolean axioms isEmpty(emptyStack()) = true isEmpty(push(x,S)) = false pop(emptyStack()) error top(emptyStack()) error pop(push(x,S)) = S top(push(x,S)) = x not isEmpty(S) => push(top(S),pop(S)) = ??? end

34 34 Implementierung von Stacks: Man kann Implementierungen von Listen verwenden. Meistens: objektorientiert (Objekte mit Zustandsänderungen). Z.B. pop : ohne Rückgabe, vgl. Skript. // Basis-Methoden: public Object top() { return this.head.getEl(); } public Object pop() { Object t = this.top(); this.head = this.head.getSucc(); return t; } public void push(Object El) { PCell h1 = new PCell(El); h1.setSucc(this.head); this.head = h1; } public boolean isEmpty() { return (this == null || this.head==null); }

35 35 Anwendungen von Stacks Unterstützung von Kontrollstrukturen, z.B. Auswertung algebraischer Ausdrücke, Verwaltung geschachtelter Prozeduraufrufe, speziell bei rekursiven Prozeduren.

36 36 Queue (Warteschlange): FIFO Die Grundoperationen auf einer Queue: front first enqueue hänge ein Element hinten an dequeue butFirst

37 37 Queues: Axiomatische ADT-Spezifikation adt Queue sorts Queue, El, boolean ops emptyQueue: Queue front: Queue El dequeue: Queue Queue enqueue: El x Queue Queue isEmpty: Queue Boolean axioms isEmpty(emptyQueue()) = true isEmpty(enqueue(x,Q)) = false isEmpty(Q) => front(enqueue(x,Q)) = x isEmpty(Q) => dequeue(enqueue(x,Q)) = Q not isEmpty(Q) => front(enqueue(x,Q)) = ??? not isEmpty(Q) => dequeue(enqueue(x,Q)) = ??? end


Herunterladen ppt "1 Kapitel 5: Von Datenstrukturen zu Abstrakten Datentypen 5.1 Zur Erinnerung: Datenstrukturen in Pascal Listenverarbeitung in Pascal 5.2 Abstrakte Datentypen."

Ähnliche Präsentationen


Google-Anzeigen