{$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.