{$APPTYPE Console} program Collections; const MaxCount = 100; type { abstrakte Klasse} tAny = class end; { abstrakte Klasse fuer Elemente einer Menge, d.h. von dieser Klasse können keine Instanzen gebildet werden. Sie dient nur zum Definieren der wirklich benoetigten Klassen. Sinn dieses Vorgehens ist, die Eigenschaften, die alle weiteren Klassen ohnehin in gleicher oder ähnlicher Form benoetigen hier genau einmal zu implementieren, anstatt sie in den weiteren Implementierungen mehrfach zu schreiben } tElement = class(tAny) public function Clone: tElement; virtual; abstract; function GetTypeId: pointer; virtual; abstract; function IsEqualP(rE: tElement): boolean; virtual; abstract; function GetAsString: string; virtual; abstract; end; tIntElement = class(tElement) private TypeId : integer; Data : integer; public constructor Create(I: integer); function Clone: tElement; override; function GetTypeId: pointer; override; function IsEqualP(rE: tElement): boolean; override; function GetAsString: string; override; end; tRealElement = class(tElement) private Data : real; public function Clone: tElement; override; constructor Create(R: real); function GetTypeId: pointer; override; function IsEqualP(rE: tElement): boolean; override; function GetAsString: string; override; end; { abstrakte Klasse fuer "Anhaeufungen" von Elementen, wie Mengen, Stringlisten, Vektoren etc.. Dieses Vorgehen ermoeglicht bei Ergaenzungen und Erweiterungen die umfassend gueltigen Codeteile durch eine Vererbungsoperation einfach weiter zu nutzen. Natuerlich koennte man die hier enthaltenen Daten und Operationen auch in die Klasse Set einfuegen, dies ginge technisch, waere aber wenig objektorientiert } tCollection = class(tAny) protected Count : integer; Elements : array[0..MaxCount] of tElement; public constructor Create; destructor Done; procedure Clear; function GetElementCount: integer; function GetElement(I: integer): tElement; procedure Insert(rE: tElement); virtual; function IsInP(rE: tElement): boolean; procedure Remove(rE: tElement); function GetAsString: string; end; { die eigentliche Mengenklasse, die dank objektorientiertem Unterbau nur Operationen enthält, die dem mathmatischen Begriff einer Menge entspricht } tSet = class(tCollection) public constructor Create; procedure Insert(rE: tElement); override; procedure Difference(var S1, S2: tSet); procedure Intersection(var S1, S2: tSet); procedure Union(var S1, S2: tSet); end; {............................................................................} { Globales Error-Handling, etwas unsauber, weil nicht objektorientiert } {............................................................................} procedure ErrorMsg(Msg: string); begin writeln(Msg); readln; halt; end; {............................................................................} { Implementierung der Integer-Klasse } {............................................................................} function tIntElement.Clone : tElement; begin Clone := tIntElement.Create(Data); end; constructor tIntElement.Create(I : integer); begin Data := I; end; function tIntElement.GetTypeId: pointer; var tIntElementTypeId : integer; begin GetTypeId := addr(TIntElementTypeId); end; function tIntElement.IsEqualP(rE : tElement) : boolean; var rI : tIntElement; begin if rE.GetTypeId <> self.GetTypeId then IsEqualP := false else begin rI := tIntElement(rE); IsEqualP := (Data = rI.Data); end; end; function tIntElement.GetAsString : string; var S : string; begin str(Data, S); GetAsString := S; end; {............................................................................} { Implementierung der Real-Klasse } {............................................................................} function tRealElement.Clone : tElement; begin Clone := tRealElement.Create(Data); end; constructor tRealElement.Create(R: real); begin Data := R; end; function tRealElement.GetTypeId: pointer; var TRealElementTypeId : integer; begin GetTypeId := addr(TRealElementTypeId); end; function tRealElement.IsEqualP(rE : tElement) : boolean; var rR : tRealElement; begin if rE.GetTypeId <> self.GetTypeId then IsEqualP := false else begin rR := tRealElement(rE); IsEqualP := (Data = rR.Data); end; end; function tRealElement.GetAsString : string; var S: string; begin str(Data, S); GetAsString := S; end; {............................................................................} { Implementierung einer Klasse fuer mehrelementige Ansammlungen } {............................................................................} { Eine Collection aufraeumen } procedure tCollection.Clear; begin while Count > 0 do begin dec(Count); Elements[Count].free; end; end; { Eine Collection erzeugen } constructor tCollection.Create; { Realisierung (hier etwas untypisch): Setzen des Elemente-Zaehlers auf 0 } begin Count := 0; end; { Eine Collection beseitigen, hier gleichbedeutend mit 'aufraeumen', da als array und nicht dynamisch realisiert } destructor tCollection.Done; begin Clear; end; { Die Anzahl der Elemente ausgeben } function tCollection.GetElementCount : integer; begin GetElementCount := Count; end; { Zugriff auf das Element an der Stelle i } function tCollection.GetElement(I : integer) : tElement; begin GetElement := Elements[I]; end; { Ein Element einfuegen} procedure tCollection.Insert(rE : tElement); begin if (Count + 1 = MaxCount) then ErrorMsg('tCollection.Insert : overflow'); Elements[Count] := rE; inc(Count); end; { Die Anwesenheit eines Elements ermitteln } function tCollection.IsInP(rE : tElement): boolean; var I : integer; begin for I := 0 to Count - 1 do begin if Elements[I].IsEqualP(rE) then begin IsInP := true; exit; end; end; IsInP := false; end; { Ein Element loeschen } procedure tCollection.Remove(rE : tElement); var I, J: integer; begin I := 0; while (I < Count) do { Element rE suchen } begin if Elements[I].IsEqualP(rE) then begin { gefunden => entfernen } for J := I to Count - 2 do { d.h.: alles um 1 nach links} Elements[J] := Elements[J + 1]; dec(Count); { ...und Anzahl um 1 reduz. } end; inc(I); end; end; { Alle Elemente einer Collection ausgeben } function tCollection.GetAsString : string; var Count1, I : integer; S : string; begin Count1 := Count - 1; for I := 0 to Count1 do begin if I > 0 { das Unmoegliche abfangen } then S := S + ', '; { den String aufaeuffeln } S := S + GetElement(I).GetAsString; end; GetAsString := S; end; {............................................................................} { Implementierung einer Klasse fuer Mengen im } {............................................................................} { Eine Menge 'eroeffnen'} constructor tSet.Create; begin inherited Create; end; { Einfügen eines Elements in die Menge } procedure tSet.Insert(rE : tElement); begin if IsInP(rE) then exit; { in einer Menge dürfen keine Duplikate sein } inherited Insert(rE); { ansonsten erfuellt d. 'UrInsert' den Zweck } end; { alle Elemente aus S1, die nicht in S2 enthalten sind } procedure tSet.Difference(var S1, S2 : tSet); var Count1, I : integer; rE : tElement; begin Clear; Count := S1.GetElementCount - 1; for I := 0 to Count1 do { wiederhole mit allen Elementen ... } begin rE := S1.GetElement(I); { nehme ein Element aus S1 } if not S2.IsInP(rE) { und teste, ob es in S2 enthalten ist } then Insert(rE.Clone); { wenn nein, dann in die Menge packen } end; end; { Elemente, die sowohl in S1 als auch in S2 enthalten sind } procedure tSet.Intersection(var S1, S2 : tSet); var Count1, I : integer; rE : tElement; begin Clear; Count1 := S1.GetElementCount - 1; for I := 0 to Count1 do { wiederhole mit allen Elementen ... } begin rE := S1.GetElement(I); { nehme ein Element aus S1 } if S2.IsInP(rE) { und teste, ob es in S2 enthalten ist } then Insert(rE.Clone); { wenn ja, dann in die Menge S packen } end; end; { Elemente, die in S1 oder in S2 enthalten sind } procedure tSet.Union(var S1, S2: tSet); var Count1, I : integer; rE : tElement; begin Clear; Count1 := S1.GetElementCount - 1; for I := 0 to Count1 do { wiederhole mit allen Elementen aus S1 } Insert(S1.GetElement(I).Clone);{ fuege sie in die Menge S ein } Count1 := S2.GetElementCount - 1; for I := 0 to Count1 do { wiederhole mit allen Elementen aus S2 } Insert(S2.GetElement(I).Clone);{ fuege sie in die Menge S ein } end; {............................................................................} { ... und das Ganze anwenden ... } {............................................................................} var S, S1, S2 : tSet; begin S1 := tSet.Create; { Bilden der Menge S1 } S1.Insert(tIntElement.Create(1)); { Einfügen von Elementen } S1.Insert(tIntElement.Create(3)); { ... } S1.Insert(tIntElement.Create(5)); { ... } S1.Insert(tRealElement.Create(6.0)); { ... } S2 := tSet.Create; { Bilden der Menge S2 } S2.Insert(tIntElement.Create(2)); { Einfügen von Elementen } S2.Insert(tIntElement.Create(3)); { ... } S2.Insert(tIntElement.Create(4)); { ... } S2.Insert(tRealElement.Create(6.0)); { ... } S := tSet.Create; { Bilden der Menge S } S.Union(S1, S2); { Vereinigung bilden } writeln('Vereinigung: ', S.GetAsString); { Out: 1, 2, 3, 4, 5, 6.0 } S.Intersection(S1, S2); { Schnittmenge bilden } writeln('Durchschnitt: ', S.GetAsString); { Out: 6.0 } S.Difference(S1, S2); { Differenz bilden } writeln('Differenz: ', S.GetAsString); { Out: 1, 3, 5 } readln; end.