Das Steuerprogramm Ein größeres Beispiel Quantitative Stückliste - das

Die semantischen Funktionen

Laden und Speichern

Wir müssen eine geeignete Tuple-Repräsentation für die Stückliste finden, um die vorhandenen Operationen fread und fwrite verwenden zu können; für jedes beschriebene Teil in der Stückliste (also in deren Argumentmenge) erzeugen wir daher den folgenden, variabel langen Datensatz:

Wir könnten die Teilnummer auch als Optional modellieren und das Ende mit nil markieren. Wir verwenden hier nur deshalb direkt den für Nat1 ungültigen Wert, damit das Beispiel nicht den Rahmen sprengt -- Sie sollten aber in einer vergleichbaren Situation an die Optionals denken!

read_plist Die Funktion read_plist soll ein Tuple aus einer Datei lesen und in das Plist-Format konvertieren. Dazu wird das Tuple sequentiell verarbeitet: das erste Element eines Datensatzes ist die Nummer des zu beschreibenden Teils pn, sie wird entnommen (mit sel_pntup) und eine (zunächst leere) Bag der Bestandteile erzeugt (mk_empty_pnbag). Nun wird das nächste Element aus dem Tuple entnommen; wenn es ungleich ZERO ist, ist es ein Bestandteil tex2html_wrap_inline8224 von pn und das nächste Element gibt seine Häufigkeit an. Jedes gefundene Paar wird der Bag hinzugefügt ( union1_pnbag), bis schließlich der Datensatz mit ZERO endet; dann wird pn mit seiner Bag in die Stückliste eingefügt (mit union1_plist). Sind noch weitere Daten im Tuple, wird das nächste Teil pn bearbeitet (diese Prüfung muß für die korrekte Bearbeitung des leeren Tuple jeweils am Anfang erfolgen). Ist die Stückliste komplett, wird das Tuple wieder gelöscht (del_pntup).

Plist read_plist (Str datafile)
* {
* Pntup flist;
* Pnbag pbag;
* Nat1 pn;
* Nat0 sub_pn;
* Nat1 length;
* Nat1 i = 1;
* Plist plist = mk_empty_plist();
flist = fread_pntup(datafile);
* length = len_pntup(flist);
while (i <= length) {
* pn = sel_pntup(flist, i++);
* sub_pn = sel_pntup(flist, i++);
* pbag = mk_empty_pnbag();
while (sub_pn != ZERO) {
* pbag = union1_pnbag(pbag, sub_pn, sel_pntup(flist, i++));
* sub_pn = sel_pntup(flist, i++);
* }
* plist = union1_plist(plist, pn, pbag);
* }
* del_pntup(flist);
* return plist;
* }
*[1.5ex]

Wenn der Lesevorgang mit fread nicht erfolgreich war, wird eine leere Stückliste zurückgegeben. Diese Funktion ist ein Konstruktor -- eventuell müssen Sie also vor dem Aufruf die Stückliste, der Sie das Resultat dieser Funktion zuweisen wollen, explizit löschen!

write_plist Die Funktion write_plist soll ein ebenes Tuple aus der strukturierten Stückliste erzeugen und diese in eine Datei schreiben. Ablauf: ein Teil pn aus der Stückliste wird gewählt (sel_dom_plist) und dem Tuple mit appr_pntup angehängt. Dann wird die zugehörige Bag mit sel_plist ermittelt und das Teil aus der Stückliste entfernt (sub1_plist); in einer Schleife werden der Reihe nach die Bestandteile tex2html_wrap_inline8224 der Bag entnommen (auf die gleiche Weise wie bei den Teilen pn) und ihre Nummern gemeinsam mit ihren Häufigkeiten dem Tuple angehängt (mit conc_pntup). Ist die Bag leer, wird dem Tuple die Ende-Kennung des Datensatzes (ZERO) angehängt und der Ablauf wiederholt sich, wenn die Stückliste noch Teile enthält (damit eine leere Stückliste korrekt bearbeitet wird, muß diese Prüfung zu Beginn erfolgen). Schließlich wird das fertige Tuple mit fwrite_pntup in die Datei geschrieben. Da die Stückliste bei diesem Vorgang zerstört wird, wird vorher eine ,,Arbeitskopie`` angelegt, ebenso von jeder Bag, die der Stückliste entnommen wird. Das Funktionsergebnis entspricht dem Regebnis von fwrite.

Bool write_plist (Plist plist, Str datafile)
* {
* Plist plwork = copy1_plist(plist);
* Pntup flist = mk_empty_pntup();
* Pnbag pbag;
* Nat1 pn, sub_pn, count;
* Bool result;
while (! is_empty_plist(plwork)) {
* pn = sel_dom_plist(plwork);
* pbag = copy1_pnbag(sel_plist(plwork, pn));
* plwork = sub1_plist(plwork, pn);
* flist = appr_pntup(flist, pn);
while (! is_empty_pnbag(pbag)) {
* sub_pn = sel_dom_pnbag(pbag);
* count = sel_pnbag(pbag, sub_pn);
* pbag = sub1_pnbag(pbag, sub_pn);
* flist = conc_pntup(flist, mk2_pntup(sub_pn, count));
* }
* flist = appr_pntup(flist, ZERO);
* }
result = fwrite_pntup(flist, datafile);
* del_pntup(flist);
* return result;
* }
*[1.5ex]

Eingabe einer Teil-Beschreibung

Die Eingabe unterteilt sich in folgende Abschnitte:

  1. Eingabe des zu beschreibenden Teils -- ist eine Teilbeschreibung schon vorhanden, wird gefragt, ob diese geändert werden soll
  2. Eingabe der Liste der Bestandteile, für jedes Teil folgende Schritte:
  3. Wenn alles korrekt war: Eintrag der kompletten Beschreibung in die Stückliste (wird ein Teil re-definiert, muß der vorherige Eintrag gelöscht werden)

Wie beendet man die Eingabe der Bestandteil-Liste ? Hier nutzen wir wieder die ungültige Teilnummer ZERO, die uns beim Laden und Speichern schon geholfen hat. Außerdem ist es wünschenswert, die Eingabe der Beschreibung abbrechen zu können, wenn ein Bestandteil noch nicht in der Stückliste enthalten ist -- vielleicht möchte man es zunächst eingeben.

new_part Die Funktion new_part gliedert sich entsprechend der oben angegebenen Abschnitte der Eingabe. Zunächst wird eine Teilnummer pn angefordert und mit is_in_plist geprüft, ob das Teil bereits beschrieben ist; wenn ja, muß die Neueingabe einer Beschreibung bestätigt werden. Dieser Ablauf wiederholt sich solange, bis entweder die Nummer ZERO für den Abbruch der Eingabe oder ein gültiges, zu beschreibendes Teil eingegeben wurde. Wurde ZERO eingegeben, wird die Funktion mit unveränderter Stückliste verlassen.

Nun wird eine leere Bag zur Aufnahme der Bestandteile erzeugt ( mk_empty_pnbag). In einer Schleife werden nun Bestandteilnummer tex2html_wrap_inline8224 und Häufigkeit eingegeben, bis die Eingabe mit der Nummer ZERO beendet oder nach Eingabe eines unbekannten Teils abgebrochen wird. Das Flag exist gibt an, ob das Teil in der Stückliste beschrieben ist ( is_in_plist); wenn nicht, wird gefragt, ob die Eingabe abgebrochen werden soll und bei positiver Anwort die Funktion mit unveränderter Stückliste verlassen. Ist das Teil aber vorhanden, wird mit is_in_pnbag geprüft, ob es bereits als Bestandteil von pn eingegeben wurde. Wenn nicht, muß nur noch geprüft werden, ob es das zu beschreibende Teil pn selbst als Bestandteil hat -- dazu wird mit der Hilfsfunktion parts_pn ein Set sämtlicher Bestandteile von tex2html_wrap_inline8224 ermittelt und mit is_in_pnset geprüft, ob das Teil pn nicht in diesem Set enthalten ist. War tex2html_wrap_inline8224 ungültig, muß die Eingabe wiederholt werden. Wurde schließlich eine gültige Nummer eingegeben (also auch nicht ZERO zum ordnungsgemäßen Ende der Eingabe), wird deren Häufigkeit erfragt, die natürlich mindestens 1 sein muß. Das vollständige Paar wird nun mit union1_pnbag der Bag von Bestandteilen hinzugefügt.

Nach erfolgreicher Beendigung der Eingabe wird die Stückliste geändert: ist das Teil pn neu (wird es also nicht re-definiert), kann das Paar pn und Bag mit union1_plist in die Stückliste eingetragen werden; sonst muß der bestehende Eintrag überschrieben und dabei gleichzeitig gelöscht werden -- das macht ovwrt1d_plist. Die Stückliste wird nun zurückgeliefert.

Auf die Prüfung der Benutzereingaben wird hier übrigens verzichtet, damit die Funktion nicht den Rahmen sprengt.

Plist new_part (Plist plist)
* {
* Pnbag pbag;
* Pnset ps;
* Bool ovwrt, exist, twice, recursive;
* int c;
* Nat0 pn, sub_pn, count;
do {
* puts("Neue Teilnummer (Abbruch mit 0):");
* scanf("%ld", &pn);
* ovwrt = is_in_plist(pn, plist);
* if (ovwrt) {
* puts("Teil ist vorhanden - neu definieren ?");
* c = toupper(getchar());
* }
* else
* c = 'J';
* } while (c != 'J');
if (pn == ZERO)
* return plist;
pbag = mk_empty_pnbag();
* do {
* do {
* printf("Bestandteil von %ld (Ende mit 0):\n", pn);
* scanf("%ld", &sub_pn);
* exist = sub_pn == ZERO || is_in_plist(sub_pn, plist);
* brk = twice = recursive = FALSE;
* if (sub_pn != ZERO)
* continue;/ tex2html_wrap_inline6338 Vorzeitiges Ende der Schleife tex2html_wrap_inline6338 /
if (! exist) {
* printf("Teil %ld nicht definiert.\n", sub_pn);
* printf("Eingabe fuer %ld abbrechen ?\n", pn);
* c = toupper(getchar());
* if (c == 'J') {
* del_pnbag(pbag);
* return plist;
* }
* }
* else {
* twice = is_in_pnbag(sub_pn, pbag);
* if (twice)
* puts("Teil ist schon angegeben.");
* else {
* ps = parts_pn(plist, sub_pn);
* recursive = is_in_pnset(pn, ps);
* del_pnset(ps);
* if (recursive)
* puts("Rekursive Beschreibung.");
* }
* }
* } while (! exist || twice || recursive);
if (sub_pn != ZERO) {
* do {
* printf("Haeufigkeit des Teils %ld:\n", sub_pn);
* scanf("%ld", &count);
* if (count < ONE)
* puts("Haeufigkeit bitte groesser als 0 !");
* } while (count < ONE);
* pbag = union1_pnbag(pbag, sub_pn, count);
* }
* } while (sub_pn != ZERO);
if (ovwrt)
* plist = ovwrt1d_plist(plist, pn, pbag);
* else
* plist = union1_plist(plist, pn, pbag);
* return plist;
* }
*[1.5ex]

parts_pn Die Hilfsfunktion parts_pn liefert, wie Sie es schon von der qualitativen Stückliste her kennen, den Set aller Bestandteile eines Teils -- dazu wird der Set aller direkten Bestandteile ermittelt und damit die Funktion parts_pnset aufgerufen, die zu einem Set den Set aller Bestandteile ermittelt. Die Funktion parts_pnset finden Sie auf parts_pn etwas anders aussieht als ihr Pendant: damit erkannt wird, ob pn direkt durch sich selbst definiert wird, wird das Teil in einen ein-elementigen Set umgewandelt; dann enthält der Set aller Bestandteile von tex2html_wrap_inline8224 auch tex2html_wrap_inline8224 selbst und ein separater Test auf Gleichheit von pn und tex2html_wrap_inline8224 kann entfallen.

Pnset parts_pn (Plist plist, Nat1 pn)
* { return parts_pnset(plist, mk_one_pnset(pn)); }
*[1.5ex]

Die von parts_pnset benötigte Hilfsfunktion subparts_pnset können wir mit Hilfe von fold für Sets auf die gleiche Weise realisieren wie für eine qualitative Stückliste -- sie finden diese Funktion auf Unterschied betrifft allerdings die Konvertierungsfunktion, die ja keine qualitative Stückliste als Argument erhält und demzufolge den Set der direkten Bestandteile eines Teils auf eine andere Weise ermitteln muß! Da der Set der direkten Bestandteile die Argumentmenge der zugeordneten Bag ist, brauchen wir eine Funktion dom_pnbag, die uns diese Argumentmenge liefert. Wie auf Maps realisierbar:

 IMPLEMENTATIONS 
 *
Pnbag=¯...
 *
		+ fold.dom(RESULT="Pnset", 
 *
				CVFCT="arg", 
 *
				NULLFCT="mk_empty", 
 *
				FOLDFCT="union")

mit der Konvertierungsfunktion
* Pnset arg_nat1_nat1 (Nat1 pn, Nat1 sub_pn)
* { return mk_one_pnset(pn); }
*[1.5ex]

Dann sieht die Konvertierungsfunktion für subparts_pnset so aus:

Pnset get_nat1 (Nat1 pn, Plist plist)
* { return dom_pnbag(copy1_pnbag(sel_plist(plist, pn))); }
*[1.5ex]

Struktur-Stückliste

Die Struktur-Stückliste für ein Teil ist rekursiv und muß daher durch eine rekursive semantische Funktion aufgebaut werden. Für Testzwecke kann ihre Ausgabe auf dem Bildschirm mit Hilfe der Operation pr_slist erfolgen, so daß wir uns auf ihre Konstruktion beschränken wollen.

Beispiel: Das Teil #10 besteht aus zwei Teilen #14 und einem Teil #3, welches unzerlegbar ist; das Teil #14 wiederum enthält einmal Teil #3, sechsmal das atomare Teil #31 und dreimal das Teil #9, welches wiederum viermal das Teil #31 enthält. Die Struktur-Stückliste hat dann folgendes schematisches Aussehen:

Teil #10
*[1ex] #14(2x) #3 (1x)
*[1ex] #3 (2x) #9(6x) #31(12x)
*[1ex] #31 (24x)

Auf jeder Stufe soll also die Anzahl der benötigten Teile angegeben sein, die von der Anzahl übergeordneter Teile abhängt; diese Angabe muß der Funktion als Argument übergeben werden. Das zu strukturierende Teil wird einmal benötigt, also ist das Argument beim erstmaligen Aufruf gleich 1. Außerdem wird für das zu strukturierende Teil auf jeder Stufe die Bag der Bestandteile ermittelt und ebenfalls an die Funktion übergeben. Damit diese Bags beim rekursiven Aufruf auch zugreifbar sind, ist die Stückliste ein weiteres, allerdings unveränderliches Argument.

build_slist Diese Funktion heißt build_slist und führt, solange die als Argument erhaltene Bag der Bestandteile nicht leer ist, folgende Schritte aus:

Zu Beginn der Funktion wird eine lokale Struktur-Stückliste im leeren Zustand erzeugt (mk_empty_slist), die dann als Resultat zurückgeliefert wird.

Slist build_slist (Pnbag pbag, Nat1 times, Plist plist)
* {
* Slist slist = mk_empty_slist();
* Pnrec prec;
* Pnbag sub_bag;
* Nat1 sub_pn, count;

while (! is_empty_pnbag(pbag)) {
* sub_pn = sel_dom_pnbag(pbag);
* count = times tex2html_wrap_inline6338 sel_pnbag(pbag, sub_pn);
* pbag = sub1_pnbag(pbag, sub_pn);
* sub_bag = copy1_pnbag(sel_plist(plist, sub_pn));
* prec = mk_pnrec(count, build_slist(sub_bag, count, plist));
* slist = union1_slist(slist, sub_pn, prec);
* }
* return slist;
* }
*[1.5ex]

Das Steuerprogramm Ein größeres Beispiel Quantitative Stückliste - das

VDM Class Library