Routenplaner

Werbung
Example 3
Architectural Design
EINFACHER
ROUTENPLANER –
DESIGNDOKUMENT
Einführung in die strukturierte Programmierung –
Übungsbeispiel 3
Gruppenmitglieder Gruppe 18
Thomas Bernat [0530058]
Andreas Hechenblaickner [0430217]
Daniela Kejzar [0310129]
Christoph Zehentner [0430513]
Gruppe 18
Seite 1
Architectural Design
1
Example 3
Architectural Design
Das zweite Übungsbeispiel „Gerichteter Graph“ wird um Funktionen zur
Ermittlung eines gültigen Pfades zwischen zwei Knoten erweitert.
1.1
Beschreibung eines Pfades
Ein Pfad zwischen zwei Knoten wird von einer Liste von Knoten beschrieben,
zwischen denen Kanten mit entsprechender Richtung existieren.
Abbildung 1.1.1: Gerichteten Graph mit mehreren Pfaden
In der Abbildung sind Pfade zwischen den Knoten (a) und (e) möglich, die
durch folgende Listen beschrieben werden:
[ (a), (b), (c), (e) ]
[ (a), (d), (e) ]
[ (a), (b), (d), (e) ]
Alle drei genannten Listen beschreiben gültige Pfade um von (a) nach (e) zu
gelangen. Die Namen der Kanten spielen dabei keine Rolle und wurden deshalb
in der Abbildung nicht eingezeichnet.
Vorsicht besteht bei Schleifen im Graphen, wie etwa zwischen den Knoten (b)
und (c). So würde zB auch
[ (a), (b), (c), (b), (c), (b), (c), ..., (e) ]
eine unendliche Menge von gültigen Pfaden von (a) nach (e) beschreiben.
Seite 2
Gruppe 18
Example 3
1.2
Architectural Design
Ermitteln des Pfades
Es gilt einen beliebigen gültigen Pfad im Graphen zwischen gegebenem Startund Zielknoten zu finden. Diese Aufgabe wird rekursiv gelöst, indem zuerst der
Startknoten in die Pfad-Liste aufgenommen wird, und dann für jede Kante die
vom Startknoten ausgeht, eine neuer Startknoten ermittelt wird. Zwischen
diesen neu gefunden Startknoten und dem alten Zielknoten wird wiederum
versucht ein Pfad zu ermitteln, bis einer der zwei folgenden Kriterien eintrifft:
1. Start- und Zielknoten sind identisch. In diesem Fall wurde ein gültiger
Pfad gefunden. Die Pfad-Liste enthält nun eine gültige Liste von Knoten
um vom ursprünglichen Startknoten zum Zielknoten zu gelangen.
2. Vom Startknoten führen keine weiteren Kanten mehr weg. In diesem
Fall befindet man sich in einer „Sackgasse“ und muss einen Schritt
zurückgehen und es mit der nächsten Kante im vorherigen Aufruf
probieren (Backtracking). Dazu wird der eben betrachtete Knoten wieder
aus der Pfad-Liste entfernt (er befindet sich immer an der letzten
Stelle).
Ein gültiger Pfad wurde gefunden, wenn Fall (1) eintritt. Kommt dieser Fall
niemals vor, gibt es keinen gültigen Pfad zwischen Start- und Zielknoten in
dem Graphen.
Anmerkung: Es wird dabei nicht der kürzeste, sondern der als erstes eingefügte
Pfad gefunden!
Besondere Vorsicht besteht bei Schleifen im Graphen (siehe oben). Um diese
im Pfad von vornherein auszuschließen, muss zunächst überprüft werden, ob
sich der aktuelle Startknoten bereits in der Pfad-Liste befindet. Ist dies der Fall,
würden weitere rekursive Aufrufe zu einer endlosen Schleife führen. Es ist
daher notwendig das beschriebene Backtracking sofort durchzuführen.
Gruppe 18
Seite 3
Architectural Design
Example 3
Struktogramm
Aus den obigen Überlegungen ergibt sich folgendes Struktogramm als
Beschreibung eines möglichen Lösungsweges:
ErmittlePfad (Startknoten, Zielknoten)
Startknoten noch nicht in Pfad enthalten?
Ja
Nein
Startknoten zu Pfad hinzufügen
Startkonten = Zielknoten?
Ja
Nein
Startknoten zu Pfad
hinzufügen
für alle Knoten zu den Kanten von
Startknoten existieren
Rückgabe
TRUE
ErmittlePfad (Knoten, Zielknoten)?
Ja
Rückgabe
TRUE
Nein
Letzten Knoten in
Pfad entfernen
Rückgabe FALSE
Rückgabe FALSE
Abbildung 1.2.1: Rekursives ermitteln eines Pfades – Struktogramm
1.3
Verwendete Datenstruktur
Ein Pfad wird in einem dynamischen Array von Zeigern auf Knoten gespeichert.
Beim ermitteln eines Pfades muss bei jedem Funktionsaufruf ein Element an
letzter Stelle hinzugefügt werden können. Beim Backtracking wird jeweils das
zuletzt eingefügte Element wieder entfernt. Die Datenstruktur entspricht daher
einem Stapel.
Structure
1 typedef struct Path_ * Path;
2
3 struct Path_ {
4
int length;
// aktuelle Anzahl der Elemente des Pfades
5
Node * nodeList;
// Array von Knoten
6 };
Seite 4
Gruppe 18
Example 3
Modul Path
2
Modul Path
2.1
Funktionalität
Erstellen eines neuen Pfades
Speicher für eine neue leere Pfad-Liste wird angelegt und initialisiert. Diese
Funktion entspricht dem Prinzip eines Konstruktors. Kann der notwendige
Speicher nicht angefordert werden, tritt der Fehler ERROR_NOT_ENOUGH_MEMORY
auf.
Funktionsheader
Path newPath();
Pfad zerstören
Gibt den verwendeten Speicher des übergebenen Pfades frei. Diese Funktion
entspricht dem Prinzip eines Destruktors.
Funktionsheader
void freePath(Path path);
Knoten an letzter Stelle zum Pfad hinzufügen
Fügt einen Knoten am Ende der Pfad-Liste (als oberstes Element im Stapel)
hinzu. Es muss zunächst der für die Pfad-Liste angeforderte Speicherblock
vergrößert werden. Kann der Block nicht vergrößert werden, tritt der Fehler
ERROR_NOT_ENOUGH_MEMORY auf. Weiters muss der Wert length entsprechend
angepasst werden.
Funktionsheader
void addNodeToPath(Path path, Node node);
Letzten Knoten vom Pfad entfernen
Löscht den letzten Knoten aus der Pfad-Liste (das oberste Element am Stapel).
Der
für
die
Pfad-Liste
angeforderte
Speicherblock
muss
entsprechend
verkleinert und der Wert length verringert werden.
Gruppe 18
Seite 5
Modul Path
Example 3
Funktionsheader
void removeLastNodeFromPath(Path path);
Überprüfen ob der Knoten bereits im Pfad ist
Überprüft ob der übergebene Knoten bereits in der Pfad-Liste enthalten ist.
Wird der Knoten gefunden, wird der Wert TRUE zurückgegeben, sonst FALSE.
Funktionsheader
int isNodeInPath(Path path, Node node);
Pfad am Bildschirm ausgeben
Gibt einen Pfad in der geforderten Schreibweise am Bildschirm aus. Die Namen
der einzelnen Knoten in der Pfad-Liste werden dabei durch '->' getrennt.
Funktionsheader
void printPath(Path path);
Verwendet
getNameFromNode (Modul Knoten)
Pfad ermitteln
Ermittelt einen beliebigen gültigen Pfad zwischen src_node und dst_node im
Graphen graph. Es muss anfangs ein leerer Pfad übergeben werden. Falls ein
Pfad gefunden wird, wird der Wert TRUE zurückgegeben und der Parameter
path enthält einen gültigen Pfad um von src_node nach dst_node zu gelangen.
Gibt es keinen gültigen Pfad im Graphen, wird FALSE zurückgegeben. Der
Parameter path sollte in diesem Fall nicht ausgewertet werden.
Seite 6
Gruppe 18
Example 3
Modul Path
Funktionsheader
int
searchPath(Graph
graph,
Node
src_node,
Node
dst_node,
Path
path);
Verwendet
searchPath
isNodeInPath
addNodeToPath
removeLastNodeFromPath
getFirstEdgeBySrc
getNextEdgeBySrc
getDstFromEdge
2.2
Testprogramm – Befehl sp (Show Path)
Das Modul Testprogramm wird um den Befehl sp zur Anzeige eines Pfades
zwischen zwei Knoten erweitert. Die Namen des Start- und des Zielknoten
werden abgefragt. Wird ein Pfad gefunden, wird dieser in folgendem Format
ausgegeben:
a->b->c->d
Wird kein Pfad gefunden, wird folgende Meldung angezeigt:
Sorry, no path!
Anmerkung: Diese Meldung wird nicht über die Fehlerbehandlungroutine
abgehandelt, da der Aufbau der Meldung leider nicht ins Schema passt.
Ist einer der beiden eingegebenen Knoten nicht im Graphen vorhanden, tritt
der Fehler ERROR_NODE_NOT_EXISTS auf.
Funktionsheader
void commandShowPath(Graph graph);
Verwendet
readLine
newPath
searchPath
printPath
freePath
errorHandler
Gruppe 18
Seite 7
Modul Path
2.3
Example 3
Fehlerbehandlung
Zur
Anzeige
von
Fehlermeldungen
wird
das
eigenständige
Modul
Fehlerbehandlung (siehe Übungsbeispiel 2) verwendet.
Fehlerkonstanten
Es werden folgende Konstanten als error_code für die Fehlerfälle definiert:
Seite 8
Fehlerkonstante
ERROR_NULL_POINTER
Fehlermeldung
Error: Null pointer!
ERROR_NOT_ENOUGH_MEMORY
Error: Not enough memory!
ERROR_NODE_NOT_EXISTS
Error: Node does not exist!
Gruppe 18
Example 3
3
Aufteilung in mehrere Dateien
Aufteilung in mehrere Dateien
Zur
besseren
Übersicht
wird
der
Quellcode
in
mehrere
Source-
und
Headerdateien aufgeteilt. Die Aufteilung entspricht dabei weitestgehend der
getroffenen Einteilung in die einzelnen Module. Zusätzlich wird ein Makefile
erstellt.
Datei
main.c, main.h
Beschreibung
aus
Hauptprogramm
Example 2
common.c, common.h
Allgemein Funktionen und Konstanten,
Fehlerbehandlungsroutine
Example 2
graph.c, graph.h
Operationen am Graphen
Example 2
node.c, node.h
Knotenliste; Operationen auf Knoten
Example 2
edge.c, edge.h
Kantenliste; Operationen auf Kanten
Example 2
path.c, path.h
Pfad zwischen zwei Knoten
Example 3
testsuite.c,
testsuite.h
Testprogramm (CLI)
Example 2,
Example 3
Makefile
GNU-Makefile
Example 3
Gruppe 18
Seite 9
Herunterladen