Informatik/Jahrgangsstufe Q/001 Klausuren/Themensortiert/02

Transcription

Informatik/Jahrgangsstufe Q/001 Klausuren/Themensortiert/02
Aufgabe 2:
dynamische Datenstrukturen – doppelt verkettete Liste
Aus dem Unterricht ist Ihnen der Datentyp der Linearen Liste bekannt. Eine doppelt
verkettete Liste ist nun ein Spezialfall einer solchen Linearen Liste in der jedes Element nicht
nur einen Zeiger auf den Nachfolger sondern auch einen Zeiger auf den Vorgänger besitzt.
Der Datenzugriff erfolgt ansonsten genau wie bei der Linearen Liste über den Zeiger Aktuell
bzw. eine entsprechende Funktion.
Gegeben ist das folgende interface:
type TElement = class
Next: TElement;
Prev: TElement;
constructor create;
end;
TIntegerElement = class(TElement)
private
sInhalt: Integer;
public
procedure SetInhalt(i: integer);
function GetInhalt: integer;
constructor create(i: integer);
end;
TTwoLinkList = class
private
sAnfang, sEnde,
sAktuell: TElement;
public
constructor create;
destructor destroy;
{ Navigationsroutinen }
procedure vor;
procedure zurueck;
procedure anAnfang;
procedure ansEnde;
{ Informationsroutinen }
function Leer: boolean;
function Aktuell: TElement;
function Anzahl: integer;
{ Datensatzoperationen }
procedure EinfuegenVor(e: TElement);
procedure EinfuegenNach(e: TElement);
procedure Loeschen;
{ Urobjekt für Listenelemente
{ Zeiger auf den Nachfolger
{ Zeiger auf den Vorgänger
}
}
}
{ von TElement abgeleiteter Typ
}
{ Inhalt vom Typ integer
}
{ Belegt den Inhalt mit i
{ Rückgabe: Inhalt
{ Initialisierung
}
}
}
{ Liste über Urtyp TElement
}
{ "Zeiger" auf erstes / letztes
{ aktuelles Element
}
}
{ Initialisierung
{ Speicher aufräumen
}
}
{
{
{
{
}
}
}
}
Aktuell
Aktuell
Aktuell
Aktuell
einen weiter bewegen
einen zurück bewegen
an den Anfang
an das Ende
{ Rückgabe: Leer: ja/nein?
}
{ Rückgabe: aktuelles Element
}
{ Rückgabe: Anzahl d. Datensätze }
{ Datensatz vor Aktuell einfügen }
{ Datensatz nach Aktuell einf.
}
{ Aktuellen Datensatz löschen
}
a) Erläutern Sie den interface-Teil für die Klassen TElement und TIntegerElement.
Begründen Sie insbesondere, wofür die Schlüsselwörter private und public benutzt
werden und warum die Verwendung hier sinnvoll ist.
b) Geben Sie auszugsweise den implementation-Teil für die Klasse TTwoLinkList an,
d. h. implementieren Sie die folgenden Methoden dieser Klasse:
destructor destroy;
procedure zurueck;
procedure ansEnde;
function Leer: boolean;
procedure EinfuegenNach(e: TElement);
{
{
{
{
{
Speicher aufräumen
Aktuell einen zurück bewegen
Aktuell an das Ende
Rückgabe: Leer: ja/nein?
Datensatz nach Aktuell einf.
}
}
}
}
}
Aufgabe 3:
dynamische Datenstrukturen – ADT Stack
Aus dem Unterricht ist dir der Datentyp der Linearen Liste bekannt. Ein Keller (auch Stack
genannt) ist nun ein Spezialfall einer solchen Linearen Liste nach dem „Last in, first out“Prinzip (LIFO), d.h. die Datenaufnahme, der Datenzugriff sowie die Datenentnahme erfolgt
nur „vorne“ bzw. „oben“ am sogenannten Top-Element!
Gegeben ist das folgende interface:
type TElement = class
{ Urobjekt für Listenelemente
}
Next: TElement;
constructor create;
function InhaltAsString: string; virtual; abstract;
end;
TIntegerElement = class(TElement)
{ von TElement abgeleiteter Typ }
Inhalt: Integer;
constructor create(i: integer);
function InhaltAsString: string; override; { Konvertierung Inhalt zu string }
end;
TStack = class
private
sTop: TElement;
public
constructor create;
destructor destroy;
procedure push(i: TElement);
procedure pop;
function top: TElement;
function empty: boolean;
end;
{ Stack über Urtyp TElement
}
{ "Zeiger" auf erstes Element
}
{
{
{
{
{
{
}
}
}
}
}
}
Konstruktor: Initialisierung
Destruktor: Speicher aufräumen
neues Element auf Stapel
oberstes Element löschen
Rückgabe: oberstes Element
Stack leer?
a) Erläutern Sie den interface-Teil für die Klassen TElement und TIntegerElement.
Begründen Sie insbesondere die Vorteile der Methode InhaltAsString.
b) Geben Sie den zu diesem interface zugehörigen implementation-Teil an, d. h.
implementieren Sie alle Methoden der drei oben aufgeführten Klassen.
c)
Erweitern Sie den interface- und implementation-Teil insofern, dass auch
Zeichenketten und Punktkoordinaten der Form (x | y) ∈ IN × IN auf dem Stack abgelegt
werden können.
d) Erweitern Sie den Datentyp TStack um eine Methode
SaveToFile(Dateiname: string); mit Hilfe derer der gesamte Stackinhalt in einer
Textdatei ausgegeben wird.
e)
Es wäre schön, wenn der Datentyp TStack auch über eine Methode LoadFromFile
verfügen würde. Dies ist bei unserer Implementierung des Stack allerdings unmöglich.
Begründen Sie. Wie müsste man den Stack umorganisieren, damit eine solche Methode
Sinn machen würde? Hinweis: Keine Implementierung, sondern nur eine Beschreibung!
Aufgabe 2:
dynamische Listenstrukturen – die Datenstruktur Stack (Keller)
a) Erläutern Sie die Unterschiede der Datenstruktur eines Stacks im Gegensatz zur
Datenstruktur einer Queue (Schlange). Nennen Sie für beide Datenstrukturen jeweils
zwei praktische Beispiele.
b) Wie muss das Einfügen eines neuen Elements in einen Stack ablaufen? Schreiben Sie
einen umgangssprachlichen Algorithmus, welchen man mit Hilfe des Modellprogramms
aus dem Unterricht nachvollziehen könnte. Achten Sie darauf, dass Ihr Algorithmus alle
Spezialfälle berücksichtigt.
Die Ausgangssituation ist wie folgt gegeben:
Dabei verweist der Zeiger Top zu jeder Zeit auf das erste (vorderste bzw. oberste)
Element.
c)
Formulieren Sie ebenso einen umgangssprachlichen Algorithmus für das Löschen aus der
Datenstruktur Stack. Berücksichtigen Sie auch hier alle Spezialfälle.
d) Implementieren Sie mit Hilfe der aus dem Unterricht bekannten
„Übersetzungsschablone“ (siehe Anlage II) die Methoden der Datenstruktur TStack.
Geben Sie für die Methode push auch eine sinnvolle Oberflächenprozedur an.
unit U_stack;
interface
type
TElement = class
z: string;
Next: TElement;
constructor create;
end;
TStack = class
private
Zgr_Top: TElement;
public
constructor create;
destructor destroy;
procedure push(e: TElement);
procedure pop;
function top: TElement;
function empty: boolean;
end;
{
{
{
{
Urobjekt für Listenelemente
Später wird dies allgemein
Nachfolger
Konstruktor wird verändert
}
}
}
}
{ Stack über Urtyp TElement
}
{ "Zeiger" auf erstes Element
}
{
{
{
{
{
{
}
}
}
}
}
}
setzt Zgr_Top auf NIL
löscht alle vorh. Elemente
neues Element auf Stapel
oberstes Element löschen
Rückgabe: oberstes Element
Stack leer?
implementation
{ **** Routinen für TElement **** }
constructor TElement.create;
begin
inherited create;
Next:= NIL;
end;
{ **** Routinen für TStack **** }
Hier sind Sie gefragt!
{ Konstruktor
}
{ ererbten Konstruktor aufrufen
{ und Nachfolger initialisieren
}
}
Aufgabe 2:
Dynamische Datenstrukturen − Ring
Aus dem Unterricht ist Ihnen der Datentyp der Linearen Liste bekannt. Eine Ring ist nun ein
Spezialfall einer solchen Linearen Liste in der das letzte Element wieder auf das erste
Element verweist. Somit gibt es keinen ausgezeichneten Anfang, sondern lediglich einen
Zeiger auf ein aktuelles Element. Ansonsten erfolgt der Datenzugriff genau wie bei der
Linearen Liste.
Gegeben ist das folgende interface:
type
TElement = class
private
sNext: TElement;
public
function Next: Telement
constructor create;
end;
{ Urobjekt für Ringelemente
}
{ Nachfolger
}
{ Rückgabe des Nachfolgers
{ Konstruktor
}
}
TRing = class
private
sAktuell: TElement;
public
constructor create;
destructor destroy;
{ Navigationsroutinen }
procedure vor;
procedure zurueck;
{ Datensatzoperationen }
procedure EinfuegenVor(e: TElement);
procedure EinfuegenNach(e: TElement);
procedure Loeschen;
{ Informationsroutinen }
function Anzahl: integer;
function Leer: boolean;
function Aktuell: TElement;
end;
a) Erläutern Sie die Datenstruktur TRing.
Gehen Sie auch auf die Bedeutung der Schlüsselwörter private und public ein.
Begründen Sie, warum das Attribut sAktuell privat deklariert ist und warum in der
Klasse TRing das Attribut sAnfang nicht mehr existiert.
b) Geben Sie auszugsweise den implementation-Teil für die Klasse TRing an, d. h.
implementieren Sie die folgenden Methoden dieser Klasse:
function Leer: boolean;
procedure EinfuegenNach(e: TElement);
procedure zurueck;
function Anzahl: integer;
destructor destroy;
Aufgabe 1:
Objektorientierte Programmierung − dynamische Datenstrukturen
Die Firma MEYER & CO möchte seine Mitarbeiterkartei im Computer verwalten. Es gibt drei
verschiedene Arten von Mitarbeitern, welche alle in dieser einen Kartei gespeichert werden
sollen. Diese sind: Festangestellter, Vorstandsmitglied und Außendienstmitarbeiter.
Für alle Mitarbeiter soll der Name und die Adresse gespeichert werden.
Für jeden Festangestellten sollen zusätzlich noch folgende drei Informationen gespeichert
werden: Grundgehalt ; Stundenlohn pro Stunde ; Arbeitszeit (Stundenanzahl pro Monat).
Vorstandsmitglieder haben dagegen keine feste Arbeitszeit, beziehen dafür aber ein festes
Gehalt, welches mindestens 3000,− € beträgt.
Freie Außendienstmitarbeiter erhalten lediglich ein Grundgehalt von genau 1000,− €
zuzüglich einer individuellen Provision.
Allen Mitarbeitern soll mit Hilfe der Kartei am Ende des Monats ein Gehalt gezahlt werden,
welches sich je nach Mitarbeitertyp unterschiedlich berechnet.
Sie sollen in dieser Aufgabe Teile der Mitarbeiterkartei modellieren und implementieren.
a) Entwerfen Sie ein UML-Klassendiagramm für die Klassen TMitarbeiter,
TFestangestellter, TVorstandsmitglied und TAussendienstler. inklusive
aller benötigten Eigenschaften und Methoden.
Implementieren Sie die Gehalt-Berechnungsmethode der Klasse TFestangestellter.
Implementieren Sie den Konstruktor der Klasse TVorstandsmitglied:
constructor TVorstandsmitglied.create(pName, pAdresse: string; pGehalt: double)
b) Gegeben ist das
nebenstehende
Klassendiagramm der
Klasse
TMitarbeiterkartei.
Diese soll alle Mitarbeiter
verwalten.
Dokumentieren Sie die
Klasse TMitarbeiterkartei.
Entwerfen Sie ein UML-Klassendiagramm zur Verdeutlichung der Verwaltung der
Mitarbeiter durch die Klasse TMitarbeiterkartei unter Verwendung der aus dem
Unterricht bekannten Klassen TElement und TLinList (Dokumentation siehe
Anlage).
Hinweis: Sie können in dem Diagramm die Klassen TElement und TLinList ohne
Methoden darstellen.
Erläutern und begründen Sie die Beziehungen in Ihrem Diagramm.
c) Der Anwender stellt an die Mitarbeiterkartei die Anfrage, welche Gehälter am Ende des
Monats insgesamt bezahlt werden müssen.
Beschreiben Sie eine Strategie für die Methode Gesamtgehaelter.
Implementieren Sie die Methode entsprechend Ihrer Strategie. Achten Sie auf eine
sinnvolle Rückgabe, falls die Mitarbeiterkartei noch leer ist.
d) In der Mitarbeiterkartei soll bei jedem Mitarbeiter zusätzlich vermerkt werden, mit
welchen anderen Mitarbeitern er befreundet ist. Dazu soll eine Klasse TFreunde
entwickelt werden, die für einen Mitarbeiter alle Freunde dieses Mitarbeiters verwaltet.
Wählen Sie eine aus dem Unterricht bekannte dynamische Datenstruktur, die sich für die
Verwaltung der Freunde eignet und begründen Sie Ihre Wahl.
Erweitern Sie das in Teil b) entwickelte UML-Klassendiagramm um die Klassen
TFreunde und die gewählte Klasse der Datenstruktur für die Verwaltung.
e) Die Klasse TFreunde soll um die Methode Stammtischeinladung erweitert
werden.
Anfrage
vorher:
nachher:
Stammtischeinladung(): string
Die Klasse Freunde hat Zugriff auf alle befreundeten Mitarbeiter.
Der zurückgegebene string umfasst alle Namen der befreundeten
Mitarbeiter.
Z. B. könnte die Rückgabe einer Stammtischeinladung lauten:
´Heinz – Otto – Frauke – Fritz´
Implementieren Sie die Methode Stammtischeinladung.
MATERIALIEN ZUR AUFGABE 1
Dokumentation der Klasse TElement
Es handelt sich um die Klasse für Objekte, die in der Datenstruktur TLinList verwaltet
werden sollen.
Konstruktor
Nachher:
create()
Ein neues Element mit next = NIL ist erstellt worden.
Anfrage
Nachher:
Next() : TElement
Gibt den Zeiger auf das nachfolgende Element zurück. Gab es kein
nachfolgendes Element, so liefert Next den Wert nil.
Dokumentation der Klasse "LinList":
Es handelt sich um eine verkettete Listenstruktur zur Verwaltung beliebiger Objekte
einer allgemeinen Basisklasse. In Delphi ist das die Klasse "TObject". Der Zeiger auf
das aktuelle Element kann mittels der Aufträge "next" und "previous" zurück sogar
vor das erste bzw. hinter das letzte Listenelement bewegt werden.
Konstruktor
Nachher
create
Eine leere Liste mit der Länge 0 ist angelegt. Der Positionszeiger
steht vor der leeren Liste.
Anfrage
Nachher
isEmpty : boolean
Die Anfrage liefert den Wert wahr, wenn die Liste keine Elemente
enthält.
Anfrage
Nachher
isBefore : boolean
Die Anfrage liefert den Wert wahr, wenn der Positionszeiger vor dem
ersten Listenelement oder vor der leeren Liste steht.
Anfrage
Nachher
isBehind : boolean
Die Anfrage liefert den Wert wahr, wenn der Positionszeiger hinter
dem letzten Listenelement oder hinter der leeren Liste steht.
Auftrag
Nachher
next
Der Positionszeiger ist um eine Position in Richtung Listenende
weitergerückt, d.h. wenn er vor der Liste stand, wird das Element am
Listenanfang zum aktuellen Element, ansonsten das jeweils
nachfolgende Listenelement. Stand der Positionszeiger auf dem
letzten Listenelement, befindet er sich jetzt hinter der Liste. Befand er
sich hinter der Liste, hat er sich nicht bewegt.
Auftrag
Nachher
previous
Der Positionszeiger ist um eine Position in Richtung Listenanfang
weitergerückt, d.h. wenn er hinter der Liste stand, wird das Element
am Listenende zum aktuellen Element, ansonsten das jeweils
vorhergehende Listenelement. Stand der Positionszeiger auf dem
ersten Listenelement, befindet er sich jetzt vor der Liste. Befand er
sich vor der Liste, hat er sich nicht bewegt.
Auftrag
Nachher
toFirst
Der Positionszeiger steht auf dem ersten Listenelement. Falls die
Liste leer ist befindet er sich jetzt hinter der Liste.
Auftrag
Nachher
toLast
Der Positionszeiger steht auf dem letzten Listenelement. Falls die
Liste leer ist befindet er sich jetzt vor der Liste.
Anfrage
Nachher
getObject: TObject
Die Anfrage liefert den Wert des aktuellen Listenelements bzw. nil,
wenn die Liste keine Elemente enthält, bzw. der Positionszeiger vor
oder hinter der Liste steht.
Auftrag
Vorher
update (pInhalt : TObject)
Die Liste ist nicht leer. Der Positionszeiger steht nicht vor oder hinter
der Liste.
Der Wert des Listenelements an der aktuellen Position ist durch den
neuen Inhalt ersetzt.
Nachher
Auftrag
Vorher
Nachher
insertBefore (pInhalt : TObject)
Der Positionszeiger steht nicht vor der Liste.
Ein neues Listenelement mit dem entsprechenden Inhalt ist angelegt
und vor der aktuellen Position in die Liste eingefügt worden. Der
Positionszeiger steht hinter dem eingefügten Element.
Auftrag
insertBehind (pInhalt : TObject)
Vorher
Nachher
Der Positionszeiger steht nicht hinter der Liste.
Ein neues Listenelement mit dem entsprechenden Inhalt ist angelegt
und hinter der aktuellen Position in die Liste eingefügt worden. Der
Positionszeiger steht vor dem eingefügten Element.
Auftrag
Vorher
Nachher
delete
Die aktuelle Position verweist auf ein Listenelement.
Das Element an der aktuellen Listenposition wird aus der
Listenstruktur entfernt. Das Inhaltsobjekt bleibt erhalten. Das
nachfolgende Listenobjekt wird dabei zum neuen aktuellen Element.
Der Positionszeiger befindet sich hinter der Liste, falls das letzte
Element der Liste gelöscht wurde.
Destruktor
Nachher
Free
Die Listenstruktur ist nicht mehr vorhanden.
Aufgabe 1:
Objektorientierte Programmierung – dynamische Datenstrukturen
Das Zwei-Personen-Kartenspiel "Längstes Leben" verläuft nach folgenden Spielregeln.
1.
2.
3.
4.
Ein Kartenspiel mit 52 Karten wird gemischt und gleichmäßig an beide Spieler verteilt.
Beide Spieler legen ihre Karten alle verdeckt aufeinander.
Gleichzeitig legen beide Spieler die oberste Karte offen auf den Tisch.
Der Spieler mit dem höheren Kartenwert nimmt sich beide Karten und steckt sie verdeckt
unter seinen Kartenstapel.
Bei Gleichstand beider Kartenwerte legen beide Spieler eine weitere Karte auf den Tisch,
welche nun über den Gesamtgewinn der Karten entscheidet.
5. Haben beide Spieler noch Karten übrig, so setzt sich das Spiel am Punkt 3 fort.
Sie sollen im folgenden Teile dieses Spiels dokumentieren, modellieren und implementieren.
a) Rechts sehen Sie Karten des Kartenspiels abgebildet. Jede Karte
verfügt Informationen über seine Kartenfarbe (Karo, Herz, Pik, Kreuz)
und seinen Kartenwert (2, 3, ..., 10, Bube, Dame, König, As).
Entwerfen Sie ein UML-Klassendiagramm der Klasse TKarte, so
dass alle wichtigen Informationen einer Spielkarte modelliert werden.
b) Die Klasse TKarte soll nun um eine Methode hatGroesserenKartenwert
erweitert werden. Gegeben ist dazu die folgende Dokumentation:
Anfrage hatGroesserenKartenwert(Vergleichskarte: TKarte)
vorher: Das Objekt Vergleichskarte existiert
nachher: Falls der eigene Kartenwert größer als der Kartenwert der Vergleichskarte ist,
so wird der Wert true zurückgegeben, andernfalls wird der Wert false
zurückgegeben. Ein Kartenwert ist größer, falls er in der Reihenfolge 2, 3, 4,
5, ..., 10, Bube, Dame, König, As weiter hinten steht.
Implementieren Sie die Methode hatGroesserenKartenwert.
Hinweis: Implementieren Sie bei Bedarf eine Hilfs-Funktion, die zu einem Kartenwert den
Rang innerhalb der Reihenfolge 2, 3, 4, 5, ..., 10, Bube, Dame, König, As berechnet.
c) Nachfolgend sehen Sie das UML-Diagramm der Klasse TSpiel. Ein Objekt dieser Klasse
ermöglicht die Simulation einer Partie Längstes Leben. Die Klassen TList, TQueue
und TElement sind hier ohne Methoden angegeben, entsprechen aber den Klassen, die
Sie aus dem Unterricht kennen (siehe Anlage). Die Klasse TKarte entspricht Ihrer in
Aufgabenteil a erstellten Klasse.
Dokumentieren Sie die Klasse TSpiel.
Begründen Sie die Wahl der Klasse TList für das Kartenspiel. Warum bieten sich die
Klassen TStack und TQueue hier nicht an?
Begründen Sie die Wahl der Klasse TQueue für die Kartenstapel der beiden Spieler.
Warum bietet sich die Klasse TStack hier nicht an?
Geben Sie die Art der Beziehungen (hat-Beziehung = Aggregation bzw.
kennt-Beziehung = Assoziation) zwischen den einzelnen Klassen an und begründen Sie
diese. Begründen Sie auch die angegebenen Multiplizitäten.
d) Implementieren Sie die Methoden kartenAusteilen und
ganzesSpielDurchfuehren der Klasse TSpiel.
Hinweis: Die Methode ganzesSpielDurchfuehren muss lediglich in richtiger
Reihenfolge und angemessener Häufigkeit die einzelnen Methoden der Klasse TSpiel
aufrufen. Zum Schluss sollte dann eine Siegerausgabe mit showMessage(string)
erfolgen.
Aufgabe 2:
dynamische Datenstrukturen
In der Anlage finden Sie die Dokumentation der Klasse TList. Diese verwaltet Objekte der
Klasse TElement, welche ihrerseits beliebige Inhaltsobjekte aufnehmen können.
Wir haben im Unterricht die lineare Liste so kennen gelernt, dass ein Objekt der Klasse
TElement einen Zeiger auf seinen Nachfolger besaß, welchen man mittels der Methode
getNext() auslesen konnte.
Im folgenden sollen Sie Teile der Linearen Liste implementieren. Allerdings sollen jetzt die
Elemente einen Zeiger auf den Nachfolger und auf den Vorgänger besitzen, so wie im
folgenden Klassendiagramm deutlich wird.
a) Erläutern Sie allgemein die Vorteile dieser Datenorganisation gegenüber der aus dem
Unterricht bekannten Datenstruktur.
Geben Sie die Methoden der Linearen Liste an, welche von der Änderung "profitieren" und
begründen Sie Ihre Wahl.
b) Implementieren Sie die Methode insertInFrontOf, ohne die Methode
insertBehind zu verwenden. Achten Sie darauf, dass sich die Liste so verhält, wie es in
der Dokumentation angegeben ist.
Dokumentation der Klasse TElement
Es handelt sich um die Klasse für Objekte, die in der Datenstruktur TList verwaltet
werden sollen.
Konstruktor
Nachher:
create()
Ein neues Element mit next = NIL ist erstellt worden.
Konstruktor create(pObject: TObject)
Nachher:
Ein neues Element mit next = NIL und Inhalt pObject ist erstellt
worden.
Auftrag
setNext(pElement: TElement) : TElement
Nachher:
Setzt den Nachfolger-Zeiger auf das Element pElement.
Anfrage
Nachher:
getNext() : TElement
Gibt den Zeiger auf das nachfolgende Element zurück. Gab es kein
nachfolgendes Element, so liefert getNext den Wert nil.
Auftrag
Nachher:
setContent(pObject: TObject) : TElement
Setzt den Inhalt des Elements auf pObject.
Anfrage
Nachher:
getContent() : TElement
Gibt das Inhaltsobjekt zurück. Wurde noch kein Inhalt definiert, so
liefert die Methode den Wert nil.
Dokumentation der Klasse TList:
Objekte der Klasse TList verwalten beliebige Objekte nach einem Listenprinzip. Ein
interner Positionszeiger wird durch die Listenstruktur bewegt, seine Position markiert
ein aktuelles Objekt. Die Lage des Positionszeigers kann abgefragt, verändert und
die Objektinhalte an den Positionen können gelesen oder verändert werden.
Konstruktor
Nachher
create
Eine leere Liste mit der Länge 0 ist angelegt. Der Positionszeiger
steht vor der leeren Liste.
Anfrage
Nachher
isEmpty : boolean
Die Anfrage liefert den Wert wahr, wenn die Liste keine Elemente
enthält.
Anfrage
Nachher
isInFrontOf : boolean
Die Anfrage liefert den Wert true, wenn der Positionszeiger vor dem
ersten Listenelement oder vor der leeren Liste steht, sonst liefert sie
den Wert false.
Anfrage
Nachher
isBehind : boolean
Die Anfrage liefert den Wert true, wenn der Positionszeiger hinter
dem letzten Listenelement oder hinter der leeren Liste steht, sonst
liefert sie den Wert false.
Auftrag
Nachher
next
Der Positionszeiger ist um eine Position in Richtung Listenende
weitergerückt, d.h. wenn er vor der Liste stand, wird das Element am
Listenanfang zum aktuellen Element, ansonsten das jeweils
nachfolgende Listenelement. Stand der Positionszeiger auf dem
letzten Listenelement, befindet er sich jetzt hinter der Liste. Befand er
sich hinter der Liste, hat er sich nicht verändert.
Auftrag
Nachher
previous
Der Positionszeiger ist um eine Position in Richtung Listenanfang
weitergerückt, d.h. wenn er hinter der Liste stand, wird das Element
am Listenende zum aktuellen Element, ansonsten das jeweils
vorhergehende Listenelement. Stand der Positionszeiger auf dem
ersten Listenelement, befindet er sich jetzt vor der Liste. Befand er
sich vor der Liste, hat er sich nicht verändert.
Auftrag
Nachher
toFirst
Der Positionszeiger steht auf dem ersten Listenelement. Falls die
Liste leer ist befindet er sich jetzt hinter der Liste.
Auftrag
Nachher
toLast
Der Positionszeiger steht auf dem letzten Listenelement. Falls die
Liste leer ist befindet er sich jetzt vor der Liste.
Anfrage
Nachher
getItem: TObject
Die Anfrage liefert den Wert des aktuellen Listenelements bzw. nil,
wenn die Liste keine Elemente enthält, bzw. der Positionszeiger vor
oder hinter der Liste steht.
Auftrag
Vorher
replace (pObject : TObject)
Die Liste ist nicht leer. Der Positionszeiger steht nicht vor oder hinter
der Liste.
Der Wert des Listenelements an der aktuellen Position ist durch
pObject ersetzt.
Nachher
Auftrag
Vorher
Nachher
insertInFrontOf (pObject : TObject)
Der Positionszeiger steht nicht vor der Liste.
Ein neues Listenelement mit dem entsprechenden Objekt ist angelegt
und vor der aktuellen Position in die Liste eingefügt worden. Der
Positionszeiger steht hinter dem eingefügten Element.
Auftrag
Vorher
Nachher
insertBehind (pObject : TObject)
Der Positionszeiger steht nicht hinter der Liste.
Ein neues Listenelement mit dem entsprechenden Objekt ist angelegt
und hinter der aktuellen Position in die Liste eingefügt worden. Der
Positionszeiger steht vor dem eingefügten Element.
Auftrag
Vorher
Nachher
.
Auftrag
Nachher
Destruktor
Nachher
remove
Die aktuelle Position verweist auf ein Listenelement.
Das aktuelle Listenelement ist gelöscht. Der Positionszeiger steht auf
dem Element hinter dem gelöschten Element, bzw. hinter der Liste,
wenn das gelöschte Element das letzte Listenelement war. Das
Inhaltsobjekt des gelöschten Listenelements existiert weiterhin.
addList(pList: TList)
Die Liste pList ist der Liste angefügt. Die übergebene Listenstruktur
von pList existiert nicht mehr.
Free
Die Listenstruktur ist nicht mehr vorhanden.
Dokumentation der Klasse TQueue
Objekte der Klasse TQueue (Schlange) verwalten beliebige Objekte nach dem FirstIn-First-Out-Prinzip, d.h. das zuerst abgelegte Element wird als erstes wieder
entnommen.
Konstruktor create
Nachher:
Eine leere Schlange ist erzeugt.
Anfrage
Nachher:
isEmpty: Boolean
Die Anfrage liefert den Wert true, wenn die Schlange keine Elemente
enthält, sonst liefert sie den Wert false.
Auftrag
Vorher:
Nachher:
enqueue (pObject:TObject)
Die Schlange ist erzeugt.
pObject ist als letztes Element in der Schlange abgelegt.
Auftrag
Vorher:
Nachher:
dequeue
Die Schlange ist nicht leer.
Das vorderste Element ist aus der Schlange entfernt.
Das Inhaltsobjekt
Anfrage
Vorher:
Nachher:
front: TObject
Die Schlange ist nicht leer.
Die Anfrage liefert das vorderste Element der Schlange. Die Schlange
istunverändert.
Destruktor
Nachher:
destroy
Die Schlange existiert nicht mehr.
Aufgabe 1:
Modellierung
Ein Supermarkt möchte ein Programm zur Verwaltung seines Artikelbestands haben. Alle
Artikel haben eine Artikelnummer und eine Artikelbezeichnung.
Der Markt unterscheidet zwischen Spielwaren, Haushaltswaren und Lebensmitteln, wobei die
Lebensmittel nochmals unterteilt sind in Lebensmittel für den menschlichen Verzehr
(Nahrungsmittel) und für den tierischen Verzehr (Tiernahrung). Zu den oben angegebenen
Informationen sollen noch zusätzliche Informationen in dem Verwaltungsprogramm
gespeichert werden:
Zu allen Spielwaren soll eine Altersbeschränkung angegeben werden können.
Alle Lebensmittel (sowohl Nahrungsmittel als auch Tiernahrung) sollen mit einem
Mindesthaltbarkeitsdatum erfasst werden.
Bei Artikeln der Kategorie Tiernahrung soll die Tierart als Information festgehalten werden.
Das Verwaltungsprogramm soll später Listen aller verwalteten Artikel ausgeben können.
Z. B. könnte der Artikelbestand des Supermarkts wie folgt gelistet sein (die in Klammern
angegebenen Nummern entsprechen der Artikelnummer):
Spielwaren - Fußball (12345), Altersbeschränkung: ab 6 Jahre
Haushaltswaren - Schneidemesser (24154)
Nahrung - Brot (12546), MHD: 5 Tage
Tiernahrung - Hund - Schappi (56879), MHD: 60 Tage
Sie sollen im folgenden Teile dieses Verwaltungsprogramms dokumentieren, modellieren und
implementieren.
a) Entwickeln Sie ausgehend der nachfolgend angegebenen, lückenhaften Klassendefinition
eines Artikels ein UML-Diagramm, welches die gegebene Artikel-Struktur modelliert.
b) Die Artikel sollen in der Datenstruktur Queue verwaltet werden. Die Software-Entwickler
haben sich folgende Klassendefinition zur Verwaltung der Artikel überlegt:
Dokumentieren Sie die Klasse Artikelverwaltung.
Begründen Sie, dass die gewählte Datenstruktur Queue für die eben dokumentierte
Funktionalität nicht besonders geeignet ist.
Entwerfen Sie ein UML-Diagramm, welches die Klassen (ohne Angabe von Attributen und
Methoden) Artikelverwaltung, Artikel, Queue und Element in
Beziehung setzt (letztere beiden Klassen sind Ihnen aus dem Unterricht bekannt). Geben
Sie die Art der Beziehungen (hat-Beziehung = Aggregation bzw. kenntBeziehung = Assoziation) zwischen den einzelnen Klassen an und begründen Sie diese.
Geben Sie auch die Multiplizitäten der Beziehungen an.
c) Die Methode listeAusgeben() der Klasse Artikelverwaltung soll eine
Artikellistung aller Artikel so wie im Einleitungstext beschreiben ausgeben.
Implementieren Sie zunächst die Methoden ausgeben() aller nicht abstrakt definierten
Lebensmittel-Klassen. Die verschiedenen Artikel sollen wie oben gezeigt auf der Console
ausgegeben werden.
Für die Ausgabe einer Artikellistung aller Artikel auf der Console soll der folgende
Algorithmus verwendet werden:
• Richte eine neue Hilfsschlange ein.
• Solange noch Artikel in der Schlange sind
tue: • Hole Artikel aus der Schlange raus und lösche ihn dort.
• Gib den Artikel auf der Console aus.
• Füge den Artikel in die Hilfsschlange ein.
• Nachdem alle Artikel ausgegeben wurde setze Schlange auf Hilfsschlange.
Implementieren Sie die Methode listeAusgeben() der Klasse
Artikelverwaltung unter Zuhilfenahme des oben angegebenen Algorithmus.
Lösung:
Aufgabe 1:
a)
b)
Dokumentation:
Konstruktor Artikelverwaltung
nachher: eine neue Artikelverwaltung ist bereitgestellt.
Auftrag hinzufügen(artikel: Artikel)
vorher:
nachher:
Die Artikelverwaltung ist erzeugt.
Der Artikel ist hinten an die Listenstruktur angefügt.
Auftrag loeschen()
vorher:
Die Artikelverwaltung ist nicht leer
nachher: Der zuerst eingegebene Artikel ist aus der Struktur entfernt.
Auftrag listeAusgeben()
vorher:
Die Artikelverwaltung ist nicht leer
nachher: Alle Artikel sind auf der Console ausgegeben.
Begründung für die schlechte Eignung der Schlange:
Die Datenstruktur ist insbesondere für die Ausgabe der Artikellistung ungeeignet. Hier sollte
man sich mit einem Positionsanzeiger durch die Liste bewegen können, um die einzelnen
Artikel der Reihe nach ausgeben zu können.
Des weiteren ist mit dieser Datenstruktur nur eine Löschung des zu erst eingegebenen Artikels
möglich. Es kann jedoch auch sinnvoll sein, einen Artikel in der Mitte der Datenstruktur zu
löschen.
UML-Diagramm aller beteiligter Klassen:
c)
Klasse Nahrungsmittel:
public void ausgeben() {
System.out.println("Nahrung - "+this.getBezeichnung()+
" ("+this.getNummer()+"), MHD: "+
this.getMhd()+" Tage");
}
Klasse Tiernahrung:
public void ausgeben() {
System.out.println("Tiernahrung - "+this.getTierart()+" - "+
this.getBezeichnung()+" ("+this.getNummer()+
"), MHD: "+this.getMhd()+" Tage");
}
Klasse Artikelverwaltung:
public void listeAusgeben() {
Queue hilf = new Queue();
while (schlange.isEmpty() == false) {
Artikel a = ((Artikel)schlange.front());
a.ausgeben();
schlange.dequeue();
hilf.enqueue(a);
}
schlange = hilf;
}
Aufgabe 2:
dynamische Listenstrukturen
Bei Warteschlangen kommt es häufig vor, dass einige Elemente aus berechtigten Gründen
nicht am Ende der Schlange eingefügt werden sollen, sondern an einem weiter vorne
gelegenen Platz eingereiht werden. Dies sind zum Beispiel bestimmte Druckaufträge in einer
Druckerwarteschlange oder Patienten mit verschiedenen Dringlichkeiten in einer Arztpraxis.
Zur Realisierung einer solchen Schlange werden die einzufügenden Objekte mit einer
ganzzahligen Priorität versehen. Je höher die Priorität, desto weiter nach vorne gelangen die
Objekte innerhalb der Schlange. Besitzen mehrere Objekte dieselbe Priorität, so soll das neue
Objekt hinter die bereits enthaltenen Objekte gleicher Priorität eingereiht werden. Aus diesem
Grund werden derartige Schlangen auch als Prioritätenwarteschlange (Priority Queue)
bezeichnet.
a) In einer Arztpraxis mit anfangs leerem Wartezimmer spielt sich folgendes Szenario ab:
• Herr Arendt erhält die Priorität 4 und betritt das Wartezimmer.
• Frau Wolff erhält die Priorität 2 und betritt das Wartezimmer.
• Frau Fritz erhält die Priorität 5 und betritt das Wartezimmer.
• Der erste Patient wird aufgerufen und verlässt das Wartezimmer.
• Herr Kluge erhält die Priorität 4 und betritt das Wartezimmer.
Stellen Sie die Belegung der Warteschlange nach jedem Einfügen eines Patienten geeignet
dar.
b) Objekte, die in eine Prioritätenwarteschlange eingefügt werden sollen, sollen von der
Klasse PriorityObject abgeleitet sein, d. h., sie sollen stets einer Klasse angehören, die
Unterklasse der Klasse PriorityObject ist. Für den Fall des Wartezimmers könnte
dies etwa eine Klasse Patient sein.
Die Klasse PriorityQueue soll ein Objekt der Klasse Queue haben. Die
einzufügenden Objekte werden also intern in der Queue gespeichert.
Entwerfen Sie ein UML-Klassendiagramm für die Klassen PriorityQueue, Queue,
PriorityObject und Patient. Geben Sie dabei alle Methoden und Beziehungen an.
Erläutern Sie kurz die Wirkung aller in PriorityQueue und PriorityObject
enthaltenen Methoden.
c) Implementieren Sie Ihre Methode zum Löschen der Datenstruktur PriorityQueue.
d) Gegeben sei die folgende Methode der Klasse PriorityQueue:
public void ___________(PriorityObject newObject) {
helpQueue = new Queue();
while ( !this.queue.isEmpty() &&
((PriorityObject)this.queue.front()).getPriority() >=
newObject.getPriority() ) {
PriorityObject helpObject = (PriorityObject)this.queue.front();
this.queue.dequeue();
helpQueue.enqueue(helpObject);
}
helpQueue.enqueue(newObject);
while (!this.queue.isEmpty()) {
PriorityObject helpObject = (PriorityObject)this.queue.front();
this.queue.dequeue();
helpQueue.enqueue(helpObject);
}
this.queue = helpQueue;
}
Analysieren Sie diese Methode, unterteilen Sie dazu die Methode in Abschnitte. Erläutern
Sie anschließend den Zweck dieser Methode.
e) In einer Praxisgemeinschaft wird für jeden behandelnden Arzt eine eigene PrioritätenWarteschlange verwaltet. Wird ein Arzt zu einem Notfall gerufen, übernimmt der Kollege
dessen Patienten.
Implementieren Sie die folgende Methode der Klasse PriorityQueue :
insertPriorityQueue(PriorityQueue pQueue),
die in eine bestehende Prioritätenwarteschlange eine andere Prioritätenwarteschlange
unter Beibehaltung der Ordnung einfügt.
Lösung:
Aufgabe 2:
dynamische Listenstrukturen
a) Die Warteschlange entwickelt sich wie
folgt:
a. Arendt/4
b. Arendt/4 – Wolff/2
c. Fritz/5 – Arendt/4 – Wolff/2
d. Arendt/4 – Wolff/2
e. Arendt/4 – Kluge/4 – Wolff/2
b) siehe rechts
Klasse PriorityQueue:
isEmpty() : boolean
Liefert true, falls die Warteschlange leer ist,
ansonsten false.
add(PriorityObject pObj) :void
Fügt pObj an der richtigen Position in die Warteschlange ein.
dequeue() : void
Entfernt das Element mit der höchsten Priorität aus der Warteschlange, falls diese nicht
leer ist.
front() :
PiorityObject Liefert das Schlangenelement mit der höchsten Priorität, falls die Schlange
nicht leer ist, ansonsten null.
Klasse PriorityObject:
setPriority(int pPriority)
Setzt die Priorität des Objektes.
getPriority() : int
Liefert die Priorität des Objektes.
c) public void dequeue() {
queue.dequeue();
}
d) Zunächst wird eine neue Schlange helpQueue erzeugt.
Danach werden alle Objekte der regulären Schlange in die neue Hilfsschlange eingefügt,
deren Priorität größer der Priorität des neu einzufügenden Objekts sind, und aus der
regulären Schlange gelöscht.
Als nächstes wird das neu einzufügende Objekt in die Hilfsschlange eingereiht.
Abschließend werden alle noch in der regulären Schlange verbliebenen Objekte in die
Hilfsschlange eingereiht.
Ganz zum Schluss wird die Hilfsschlange als reguläre Schlange übernommen.
Ein neues Objekt wird gemäß seiner Priorität in die Schlange eingereiht.
e) public void insertPriorityQueue(PriorityQueue pQueue){
while (! pQueue.isEmpty() ){
this.add(pQueue.front());
pQueue.dequeue();
}
}