Programmieren Sie strukturiert – Teil 4: Die Modulbausteine
Computerprogramme werden öfter gelesen als geschrieben. Deshalb müssen sie »menschenfreundlich« geschrieben sein. Das heißt: sie müssen strukturiert programmiert sein.
Strukturiertes Programmieren bedeutet zuerst strukturiertes Denken und anschließend daran strukturiertes Codieren. Zum ersten braucht man den gesunden Menschenverstand, zum letzteren spezielle Werkzeuge, genauer: spezielle Befehlsstrukturen. Bisher haben wir besprochen: die Steuerbausteine (Sequenzen, Schleifen, Verzweigungen), die den Programmfluß lenken, und die Unterprogrammbausteine (Prozeduren und Funktionen), mit deren Hilfe wir fehlende Befehle herstellen können. Steuerbausteine und Unterprogrammbausteine reichen allerdings nicht aus, um ein Programm menschenfreundlich zu gestalten. Es gibt Pascal-Programme, die sind so lesbar wie hethitische Tontafeln, und Comal-Programme, deren Wege sind so verschlungen, daß ein Spaghettiprogramm daneben klar wie Quellwasser wirkt — und doch gelten beide Sprachen als »strukturiert«. Ein Programm wird erst dann für den Menschen lesbar und verstehbar, wenn nicht nur die Teile durchstrukturiert sind, sondern auch das Programm als Ganzes. Das ist wie bei einem Zeitschriftenartikel — es genügt nicht, wenn die einzelnen Absätze zwar in sich stimmig, aber ansonsten wild durcheinandergewürfelt sind. Sie müssen vielmehr in einer sinnvollen Reihenfolge stehen und in Gruppen zusammengefaßt sein, zum Beispiel in Kapiteln.
Das Programm »COMALCHEN«
Was dem Zeitschriftenartikel seine Kapitel, das sind dem Computerprogramm seine Modulbausteine. Und so wie es verschiedene Typen von Kapiteln gibt (zum Beispiel Einleitungskapitel, Schlußkapitel, »normale« Kapitel), so gibt es auch verschiedene Typen von Modulbausteinen, die jeweils speziellen Zwecken dienen. Darum soll es heute gehen.
Zur Illustration benutzen wir ein Programm, das ich »COMALCHEN« genannt habe (Listing 1). Die Anregung dazu stammt übrigens aus der 64er. Im Novemberheft 1984 wurde ein Programmwettbewerb ausgeschrieben. Gewünscht war ein »intelligentes« Programm, das offenbar die Einsamkeit der 64'er-Redakteure mildern sollte, jedenfalls wollten sie sich damit unterhalten können. Ich dachte, den Leuten muß geholfen werden und schrieb ein kleines Comal-Programm, mit dem man sich über Comal unterhalten kann, genauer, das man über Comal ausfragen kann. Leider vergaß ich dann, das Programm auch einzureichen, und so sind mir vielleicht gar die tausend Mark Preisgeld durch die Lappen gegangen.
Aber hier ist das Programm nun endlich doch. Und diesmal in Basic. Im Basic 7.0 des Commodore 128. Ich dachte, es könnte vielleicht reizvoll sein, die Möglichkeiten des (gegenüber dem C 64) erweiterten Basic einmal zu testen.
Die neuen Möglichkeiten des C 128liegen zunächst im Bereich der Steuerbausteine (vergleiche 64'er 1/86). Neu ist zum Beispiel der Sequenzbaustein
BEGIN — BEND
Dieser Baustein ist allerdings nur beschränkt einsetzbar, nur in einer einzigen Situation, nämlich innerhalb der IF-Struktur.
Was die IF-Struktur angeht, so ist diese nunmehr endlich durch ELSE-Befehl erweitert worden. Beides, das heißt der Sequenzbaustein und der ELSE-Befehl in Kombination, machen im neuen Basic eine einfache und einigermaßen übersichtliche Codierung von »Abstechern« und »Gabelungen« möglich (vergleiche zum Beispiel »COMALCHEN«, Zeilen 40220 bis 40250).
Was die Mehrfachverzweigung angeht (CASE- und IF ELIF-Struktur), so müssen wir weiterhin bei den selbstgestrickten Bausteintypen bleiben (vergleiche etwa 14020 bis 14120).
Der eigentliche Fortschritt von Basic 7.0 ist bei den Schleifen zu verzeichnen. Die Befehlselemente DO, LOOP UNTIL, WHILE und EXIT erlauben es, sämtliche bisher fehlenden Schleifentypen zu bauen (Tabelle 1):
| UNTIL | WHILE | LOOP | ENDLOS |
|---|---|---|---|
| DO | DO WHILE | DO | DO |
| ... | ... | ... | ... |
| ... | ... | EXIT | ... |
| ... | ... | ... | ... |
| LOOP UNTIL | LOOP | LOOP | LOOP |
| vergleiche 160 bis 190 | vergleiche 40030 |
Ob der Befehlsname LOOP als Endmarkierung sehr geschickt gewählt ist, sei dahingestellt; man kann es lernen. Warum die Schleifen allerdings so fürchterlich langsam sein müssen (mehr als 10 Sekunden für 1000 Durchläufe), das ist nicht ganz einsichtig(Comal schafft es in einem Fünftel der Zeit!). Ubrigens kann UNTIL auch nach DO stehen und WHILE nach LOOP und eine Schleife kann sowohl EXIT enthalten als auch UNTIL und/oder WHILE. Das läßt dem Programmierer alle Freiheiten, aber bürdet ihm gleichzeitig die ganze Verantwortung auf. Das kann leicht gefährlich werden. Leidvoll erfahrenes Beispiel: Die UNTIL-Schleife ist an sich so definiert, daß sie auf jeden Fall einmal durchlaufen wird — so lange, bis die Bedingung erfüllt ist. Wenn man nun »DO UNTIL Bedingung« codiert (statt »LOOP UNTIL Bedingung«), dann wird die Bedingung vor dem Schleifendurchlauf überprüft und allerhand zunächst unerklärliche Fehler können auftreten.
Was die Unterprogrammbausteine angeht, so hat sich nichts geändert. Wir müssen also weiterhin unsere hausgemachten Prozedur- und Funktionsstrukturen benutzen, wenn wir neue Befehle brauchen (vergleiche 64'er 2/86 und 4/86).
Dazugekommen sind aber eine Reihe neuer Handlungsbefehle, wie RESTORE und Zeilennummer und Funktionsbefehle wie INSTR. Mit dem einen können wir das Programm zu einer bestimmten DATA-Zeile schicken, der andere findet heraus, ob ein String in einem anderen enthalten ist. Beide Befehle werden uns zustatten kommen. Daß sie vorgefertigt sind, erspart uns, selber Hand anzulegen, um sie zu erfinden.
Spezielle Modulbausteine sind in Basic 7.0 natürlich nicht verfügbar. Die wollen wir also nun entwickeln.
Wir machen einen Plan
Gehen wir zuerst in den Wilden Westen. Ein Fluß, fruchtbares Land, keiner erhebt Anspruch. Hier wollen wir unsere Hütte bauen. Als erstes zeichnen wir uns einen Plan.
Bild 1 zeigt, welchen Ausschnitt aus der weiten Landschaft wir uns für unser Grundstück vorstellen und welche Form es haben soll.

Damit haben wir einen vollständigen Plan unserer künftigen Heimstatt. Zugegeben, der Plan scheint nicht sehr informativ, aber das heißt nicht, daß er unvollständig wäre; alle Informationen sind im Plan enthalten. Sie sind nur noch nicht im Detail zu erkennen.
In ähnlicher Weise können wir unser Computerprogramm planen. Aus der unendlichen Weite der begrenzten Möglichkeiten schneiden wir einen kleinen Ausschnitt heraus, in dem wir uns niederlassen wollen und bezeichnen ihn (so wie wir oben unser Grundstück gezeichnet haben) mit einem der Umrisse andeutenden Wort: »COMAL-CHEN«. Unser Grundstücksplan ließ erkennen, wo das Grundstück liegen und wie es aussehen sollte. »COMALCHEN« informiert uns darüber, daß es um Comal gehen müßte.
Stecken wir in derselben Weise unseren Programmplan ab. Wir schalten den Computer an und tippen ein:
10 REM comalchen
Und da ist auch schon unser Programm! Noch ist nicht viel von seiner künftigen Gestalt zu erkennen. Probieren Sie es aus: Geben Sie RUN ein, und wenn Sie keinen Tippfehler in REM gemacht haben, läuft es von Anfang bis Ende ohne Probleme durch. Haben Sie doch einen Tippfehler gemacht — er ist leicht zu finden und leicht zu ändern.
Ein vollkommenes Programm also, allerdings noch nicht fertig. Aber was heißt eigentlich »fertig«, wann ist ein Programm fertig?
Ein Programm ist nie fertig
Wann ist unser Zuhause fertig? Wenn unsere Hütte auf dem Grundstück steht? Wenn ein Bett drin ist? Wenn ein Kissen im Bett liegt? Wenn das Kissen einen Bezug hat? Wenn in dem Bezug ein rotes Herz eingestickt ist?
Wenn wir es genau bedenken, dann kann immer etwas Neues dazukommen, noch eine Einzelheit, noch ein Detail, davon wird aber der Gesamtplan nicht betroffen. Ob später einmal ein Herzchen auf dem Kissen prangt, berührt nicht die Form unseres Grundstücks.
Wenn es aber so ist, daß die letzten Details für den Umriß irrelevant sind, dann können wir grundsätzlich das Nachdenken darüber hinausschieben. Das hat einige Vorteile. Davon gleich.
Prinzipiell entwickeln wir also unseren Plan so, daß wir mit allgemeinen Umrissen anfangen und die Umrisse dann allmählich und schrittweise mit notwendigen Details auffüllen. Im Grundstücksplan zeichnen wir also als nächstes die Lage der Hütte ein, ohne uns schon darum zu bekümmern, wo die Türen, wo die Fenster sein sollen. Wir planen, daß der Rest des Grundstücks Garten sein soll, verschieben aber die Entscheidung, welche Bäume und Büsche dort wachsen sollen und vor allem, wo sie wachsen sollen, auf später. Kommt Zeit, kommt Rat. »COMALCHEN« wird folgendermaßen detailliert: 0. Vorbereitung
1. Begruessung
2. Fragen
3. Antworten
4. Abschied
5. Programmende
Wir haben überlegt: Bevor das Programm anfangen kann, muß es Variablen festlegen, Felder dimensionieren, und was sonst noch alles nötig ist. Wir brauchen also einen Vorbereitungsprogrammteil. Was genau dort zu geschehen hat, das braucht uns noch nicht zu kümmern.
Daß Computer und Benutzer sich begrüßen werden, das gehört sich bei einem Programm, mit dem man sich unterhalten will. Also wird dafür ein Programmschritt »Begrüßung« vorgesehen — umrißhaft zunächst nur, denn wie die Begrüßung vor sich gehen wird, wissen wir zu diesem Zeitpunkt noch nicht, brauchen wir nicht zu wissen. Tatsächlich habe ich diesen Teil, als ich »COMALCHEN« schrieb, an allerletzter Stelle in Angriff genommen, als der Rest des Programms längst stand, will sagen: lief.
Das Gegenstück zur Begrüßung ist ein Programmteil »Abschied«, das Gegenstück zur Vorbereitung ist das geordnete Beenden des Programms, zum Beispiel das Zurücksetzen des Computers in den Normalzustand.
Zwischen Begrüßung und Abschied steht der Kern des Programms — der Benutzer gibt eine Frage ein und der Computer reagiert darauf mit einer Antwort. In diesem Programmbereich werden natürlich die größten Probleme liegen: Was mache ich mit der eingegebenen Frage? Wie analysiere ich sie? Wie bekomme ich den Computer dazu, eine Frage zu »verstehen« Was heißt eigentlich »verstehen® Wie entscheide ich, was für eine Antwort auf welche Frage erfolgen soll? Wo bekomme ich überhaupt Antworten her?
Das Zentralmodul
Bevor wir uns diesem Dickicht von Problemen weiter nähern, wollen wir erst den bisherigen Stand unseres Programmplans in die Wirklichkeit umsetzen. Die Idee, daß wir uns um Einzelheiten erst später kümmern, setzen wir so um, daß wir dafür Subroutinen benutzen. Unser Programm (Listing 2):
10 rem comalchen
60 :
110 rem zentralmodul
130 : gosub 1000 vorbe
reitung
140 : gosub 2000 be
gruessung
170 : gosub 3000 frage
180 : gosub 4010 antwort
210 : gosub 5000 ab
schied
220 : gosub 6000 pro
grammende
240 rem ende zentral
modul
Damit haben wir auch schon unseren ersten Modulbausteintyp kennengelernt, den jedes Programm enthält: das ZENTRALMODUL.
Die Grundform eines solchen Zentralmoduls ist:
REM zentralmodul
...
...
REM ENDE zentralmodul
Es empfiehlt sich, bei jedem Modul, wenn man es eingetippt hat, immer gleich auszuprobieren, ob es ohne Fehler läuft und ob es tut, was es soll. Fehler sind dann leicht erkennbar, ihre Ursachen auf Anhieb auffindbar und ohne viel Aufwand behebbar. Und wenn dann das Programm »fertig« ist, macht es kaum noch Probleme, und die Phase des »debugging«, die oft ein Vielfaches der Zeit in Anspruch nimmt, die man für das »eigentliche« Programmieren benötigte, wird radikal verkürzt.
Damit wir unser Modul testen können, müssen wir dafür sorgen, daß die GOSUBs jeweils ihr Ziel finden und heil wieder zurückkehren können. Wir versehen zu diesem Zweck die Zielzeilen einfach für den Augenblick mit dem Befehl RETURN, also
1000 return
2000 return
Der RUN-Test zeigt, daß unser Modul keinen Fehler enthält. Und das, obwohl ich einen kleinen Trick angewendet habe. Um der leichteren Lesbarkeit willen habe ich nicht codiert:
gosub 3000 : rem fragen
sondern so:
gosub 3000 fragen
Mit diesem Verfahren könnte man die Zeile sogar fortsetzen: gosub 3000 fragen : gosub 4000 antwort was mit REM nicht funktionierte, da alles, was nach REM kommt, vom Programm ignoriert wird.
Eine Warnung allerdings am Platze. Dieser Trick funktioniert in Commodore-Basic und zum Beispiel auch auf dem IBM-PC. Er funktioniert aber nicht in vielen anderen BASICs, und Compiler steigen regelmäßig aus, wenn ihnen solcher Unverstand begegnet.
Das Verfahren der schrittweisen Verfeinerung
Die Methode, die wir beim Planen unseres Programms benutzen und bei der das Nachdenken über Details so lange wie möglich hinausgeschoben wird, ist das Verfahren der schrittweisen Verfeinerung, auch »top-down«-Programmierung genannt. Man zerlegt dabei ein Problem in seine Teilprobleme, die Teilprobleme wiederum in Teilprobleme, und so weiter, und so fort — das geschieht so lange, bis die Probleme so einfach geworden sind, daß ihre Lösung quasi auf der Hand liegt. Im Idealfall lassen sich die »letzten« Probleme durch die Anwendung eines einzigen Computerbefehls lösen, zum Beispiel:
Problem: Programm beenden
Lösung: END
Versuchen wir, diesem Idealfall näherzukommen. Wir nehmen uns unseren Programmplan ein drittes Mal vor. Nach der neuen Bearbeitung sieht der Plan nunmehr so aus:
COMALCHEN
0. VORBEREITUNG
0.1 System konfigurieren
0.2 Initialisierungen
0.3 Funktionen definieren
0.4 Daten lesen
1. BEGRÜSSUNG
1.1 Vorstellung
1.2 Begrüssen
1.3 Unterhaltung beginnen
2. FRAGEN
2.1 Eingabe Frage
2.2 Fragentyp feststellen
2.3 Schlüsselwort suchen
3 ANTWORTEN
3.1 Antworttyp festlegen
3.2 Antwort wählen
3.3 Antwort ausgeben
4. ABSCHIED
4.1 Abschiedskommentar geben
5. PROGRAMMENDE
Eine solche Verfeinerung im Programmplan läßt sich übrigens sehr gut mit einem Textverarbeitungsprogramm durchführen, zum Beispiel mit Paperclip, mit dem man mit einem Tastendruck die Textzeilen für die Unterpunkte einfügen kann.
Natürlich wollen wir gleich sehen, wie die neuen Elemente unseres Plans in der Wirklichkeit des Programms aussehen. Am Beispiel des Punktes »2. FRAGEN« sei die Umsetzung demonstriert (Listing 3).
3000 rem modul frage
3010 : gosub 11000 ein
gabe frage
3020 : gosub fragentyp
feststellen
3030 : gosub 11500
schluesselwort
suchen
3040 return
3050 ende modul frage
Man sieht: Der Programmtext ist zu diesem Zeitpunkt noch eine fast wörtliche Übernahme des Programmplans.
Listing 3 illustriert auch gleich den zweiten Modultyp, den wir benutzen wollen, das HAUPT-MODUL. Alle Hauptpunkte eines Programms führen zu Hauptmodulen. Die Hauptmodule von »COMALCHEN« finden Sie in den Zeilen 1000 bis 9999 des Gesamtlistings.
Die allgemeine Form des Hauptmoduls:
REM modul
...
...
RETURN
REM ende modul
Hauptmodule (sowie einige andere Modultypen) enthalten den RETURN-Befehl: Da Basic keine spezielle Modulstruktur zur Verfügung stellt, müssen wir mit den Pfunden wuchern, die vorhanden sind. Das sind in diesem Fall die Befehlselemente der Subroutinenstruktur. Diese dienten uns auch schon als Steine beim Bau unserer selbstentwickelten Prozedurstruktur.
Neben- und Untermodule
Jedes Hauptmodul hat als Pendant sein NEBENMODUL. Haupt- und Nebenmodul bilden eine Art Tandem.
2030 : gosub 10500 unterhaltung beginnen
10500 rem u-modul unterhaltung beginnen
10510 : print
10520 : print"Du willst dich
also mit mir ueber COMAL unterhalten.
10530 : print
10540 : print"Ok. Ich hoere.
10550 : print
10670 return
10680 ende u-modul unterhaltung
Während im Hauptmodul bestimmt wird, was der Reihe nach getan werden muß, wird es im Nebenmodul tatsächlich getan. Das Hauptmodul gibt die Anweisung, zum Beispiel UNTERHALTUNG BEGINNEN; im Nebenmodul wird die Anweisung in die Tat umgesetzt, wird abgearbeitet. Die Arbeit wird von einer Gruppe von Basic-Befehlen durchgeführt. Diese Gruppe wird zusammengefaßt in einem UNTERMODUL. Die Anweisung UNTERHALTUNG BEGINNEN führt also zu einem Untermodul UNTERHALTUNG BEGINNEN. Listing 4 soll dies verdeutlichen. Die allgemeine Form des Untermoduls ist:
REM u-modul
...
...
RETURN
REM ende u-modul
Jeder GOSUB-Anweisung im Hauptmodul entspricht also ein Untermodul im zugehörigen Nebenmodul. Während das Hauptmodul aus einer Gruppe von GOSUB-Anweisungen besteht, ist das Nebenmodul parallel dazu aus einer Gruppe von Untermodulen aufgebaut.
Lassen Sie uns eine Zwischenbilanz machen: Ein Programm besteht aus einem Zentralmodul, das die grundlegenden Aufgaben definiert. Es enthält sodann eine Gruppe von Hauptmodulen, die fest mit zugehörigen Nebenmodulen verbunden sind. Bild 2 illustriert dies anhand des Programms »COMAL-CHEN« Den Punkt FRAGE schauen wir uns dabei mit einer schwach vergrößernden Lupe an.

Zur weiteren Verdeutlichung ein Beispiel aus dem Leben. Wir stellen uns eine Fabrik vor. Im Zentralmodul sitzt das Management; dort wird entschieden, was produziert werden soll, dort werden die grundlegenden Ablaufpläne erstellt. Die Gruppe der Haupt- und Nebenmodule sind die einzelnen Produktionsabteilungen. Im Hauptmodul sitzt jeweils der Abteilungsleiter, der die Arbeiten anordnet, die im einzelnen zu erledigen sind. Das Nebenmodul ist das eigentliche Fabrikgebäude, hier wird die Arbeit gemacht. Das Fabrikgebäude ist in mehrere Werkstätten aufgegliedert, die jeweils Teilaufgaben erledigen, das sind die Untermodule Am Beispiel des Nebenmoduls FRAGE (11000 bis 14999): Die einzelnen Untermodule haben folgende Aufgaben zu lösen: 1. eine Frage entgegennehmen; 2. feststellen, zu welchem Typ sie gehört; 3. herausfinden, ob sie eines der Schlüsselwörter enthält, aus denen man schließen kann, worauf sich die Frage bezieht (vergleiche auch Bild 2).
Gelegentlich kann man auch einmal ohne Nebenmodul auskommen — wenn es Ökonomischer scheint, daß der Chef eine einfache Aufgabe gleich selber erledigt. Das ist zum Beispiel der Fall im Hauptmodul PROGRAMMENDE.
Der Informationsfluß
Die Teilaufgaben, die in einem Untermodul zu erledigen sind, bestehen oft darin, Informationen, die hereinkommen, zu bearbeiten und auf diese Weise neue Informationen zu gewinnen, die dann weitergegeben werden. Das Untermodul FRA-GENTYP FESTSTELLEN etwa erhält die Eingabe (zum Beispiel »Was ist COMAL?«, untersucht sie und findet dabei heraus, daß sie mit dem Fragewort »was« beginnt, daß sie somit zum Typ W-Fragen gehört.
In jeder Werkstatt muß man genau wissen, welche Information hereinkommt und welche wieder hinausgehen muß. Nur dann, wenn dies ganz klar ist, können eventuelle Fehler im Produktionsprozeß ohne großen Aufwand erkannt und ausgemerzt werden. Informationen, die hereinkommen und verarbeitet werden müssen oder die zur Arbeit notwendig sind, werden zu Beginn des Untermoduls in einer IMPORT-Liste ausgeführt. Informationen, die erzeugt worden sind und herausgehen, weil sie andernorts gebraucht werden, sind in der EXPORT-Liste enthalten. Vergleichen Sie als Beispiel das Untermodul ANTWORT WAEHLEN (15400 bis 15499):
15410 rem import: typantwort
15415 rem export: antwort$
Zur Aufbewahrung von Informationen und zu ihrem Transport werden im Programm Variablen benutzt, wie wir wissen. Daher folgt nun ein Wort zur Form der Variablennamen an den verschiedenen Orten des Programms. Variablen, die Informationen enthalten, welche überall im Programm gelten (auf dem gesamten Fabrikgelände, in sämtlichen Gebäuden), heißen »global«. Sie erhalten »normale« Namen wie FRAGE$, ANTWORT$, TYPANTWORT und so weiter. Globale Variablen spielen auch in manchen Prozeduren und Funktionen eine Rolle. Wo dies der Fall ist, enthalten auch diese IMPORT und EXPORT-Listen (zum Beispiel die Prozedur ANTWORT ERGAENZEN, 44000 bis 44099).
Neben globalen Variablen gibt es auch solche, die nur innerhalb eines Untermoduls gelten, die also gewissermaßen werkstattintern gebraucht werden. Solche Variablen sind mit M (für Modul) gekennzeichnet. Im Untermodul BEGRUESSEN (10200 bis 10399) finden sich zum Beispiel folgende modulinterne Variablen: MEINGABE, MGRUSS, MOK. Wie wir aus der letzten Folge über die Unterprogrammbausteine wissen, bezeichnet man Variablen, deren Gültigkeitsbereich örtlich eingeschränkt ist, als »lokale« Variablen. Wir kannten sie allerdings bisher nur in Prozeduren, wo wir sie mit U gekennzeichnet haben. U-Variablen gibt es in diesem Programm natürlich auch. Vergleichen Sie etwa die Prozedur OBERBEGRIFF FINDEN (14500 bis 14799), wo folgende prozedurinterne Variablen vorkommen: USCHLUESSEL$, UPOSITION UWORT().
Die Rolle von Prozeduren
Zurück zu den Untermodulen, zu den Werkstätten im Fabrikgebäude des Nebenmoduls. Manchmal enthält ein Arbeitsauftrag Teile, die einen Spezialisten erfordern, der in der Werkstatt nicht zur Verfügung steht. Bevor man sich selbst damit abmüht, formuliert man besser einen Spezialauftrag und delegiert ihn. Das geschieht zum Beispiel im Untermodul EINGABE FRAGE (11020 bis 11100), wo der eigentliche Eingabevorgang an die Prozedur EINGABE delegiert wird. Wenn wir eine Basic-Version benutzen würden, die einen entsprechenden vorgefertigten Befehl besitzt, Könnten wir die Aufgabe in der Werkstatt selbstlösen. So aber sind wir gezwungen, einen eigenen Befehl zu bauen, das heißt eine Prozedur zu schreiben.
Nachdem der Spezialauftrag erledigt, die Frage in der Werkstatt, im Untermodul, eingegangen ist, wird sie gleich einmal »außer Haus« gegeben. Sie soll so zurechtgehauen werden, daß sie einem bestimmten Standard entspricht. Dazu wird sie der Prozedur FORM STANDARDISIEREN zugeschoben. Mit diesem Trick hält man sich Programmprobleme, die immer noch zu komplex sind, als daß die Lösung schon deutlich auf der Hand läge, noch eine Weile länger vom Hals — und das ist ja unser Bestreben.
Es stellt sich dann heraus, daß die Aufgabe, die die Prozedur FORM STANDARDISIEREN (12500 bis 12599) zu erledigen hat, immer noch sehr komplex ist, so daß es sinnvoll erscheint, sie noch einmal in Teilaufgaben zu zergliedern: in die Behandlung von überflüssigen Leerzeichen; die Umwandlung aller Großbuchstaben in Kleinbuchstaben und die Umwandlung der Umlaute »ä», »ö«, »ü« und des »ß« in die Zeichenfolgen »ae«, »OE«, HUE«, »35« — denn es könnte ja sein, daß der Benutzer die DIN-Tastatur des Commodore 128 benutzt. Diese Teilaufgaben werden durch Prozeduren erledigt, die in den Zeilen 40000 bis 40500 definiert sind.
Wir haben eben gesehen, daß Nebenmodule auch Prozeduren enthalten. Es sind die Prozeduren, die nur im jeweiligen Nebenmodul gebraucht und zu Hilfe gerufen werden. Die Spezialisten arbeiten in dem Gebäude, in dem man sie braucht; das ist ökonomisch, spart Zeit und macht den Produktionsvorgang übersichtlich.
Prozeduren- und Funktionenmodule
Esgibtaber auch Spezialisten, die von mehreren Produktionsabteillungen zu wechselnden Zeiten angefordert werden, externe Zulieferbetriebe, die mal für diesen arbeiten, mal für jenen. Diese sind an einem anderen Ortzusammengefaßt. So entsteht ein weiterer Modultyp, das PROZEDURENMODUL (4000 bis 45999), das alle Prozeduren enthält, die nicht eindeutig einem bestimmten Nebenmodul zugeordnet werden können.
Analog zum Prozedurenmodul gibt es ein FUNKTIONENMO-DUL, das alle Funktionsdefinitionen enthält (46000 bis 46999). Die Form dieser beiden Module:
REM prozedurenmodul
...
...
...
REM ende prozedurenmodul
REM funktionenmodul
...
...
...
RETURN REM ende funktionenmodul
Prozeduren- und Funktionenmodul fassen, so kann man sagen, die allgemeingültigen unserer selbstgestrickten Befehle zusammen.
Datenmodul und Informationsmodul
Bleibt noch das DATENMODUL zu erwähnen, das die DATA-Zeilen enthält (55000 bis 59999) und das INFORMA-TIONSMODUL (60000-) mit internen Informationen zum Programm.
Damit haben wir die Modultypen kennengelernt, aus denen das Programm »COMALCHEN« besteht. Sie fragen, woher eigentlich die Idee der Module kommt?
»modules« — »packages«
Die Anregung, Programme in Form von Modulen zu strukturieren, stammt aus einigen moderneren Programmiersprachen. So gibt es in Modula2 »modules« als strukturelle Einheiten für komplexere Aufgaben; ADA benutzt das Konzept der »packages«, und Comal 2.0 kennt »machine language packages«, die dazu dienen, eine Gruppe zusammengehöriger Assemblerprogramme zusammenzufassen. Mit ihren »modules« oder »packages« verfolgen die einzelnen Programmiersprachen unterschiedliche Ziele. Gemeinsam ist diesen Ideen aber die Erkenntnis, daß es über der Ebene der Prozeduren und Funktionen noch weitere höhere Programmstrukturen geben müsse, um die immer komplexer werdenden Programmierprobleme menschen- und wartungsfreundlich lösen zu können.
Es ist allein diese Grundidee, die dem Vorschlag, auch Basic-Programme mit Hilfe von Modulen leserfreundlicher zu gestalten, zugrundeliegt. Ansonsten haben die vorgeschlagenen Modultypen und ihre äußere Form weder etwas mit den »modules« in Modula2 noch mit den »packages« in ADA oder Comal zu tun.
Sie sind frei erfunden und Ähnlichkeiten sind eher zufällig als beabsichtigt.
Wenn aber die beschriebenen Modultypen von mir frei erfunden wurden, dann besteht kein Grund, warum Sie nicht Ihre eigenen erfinden sollten. Mein Vorschlag ist ein Vorschlag — mehr nicht. Er kann ergänzt werden durch weitere Typen (in anderen Programmen benötigt man zum Beispiel zusätzlich ein Menümodul); die äußere Form der Module kann umgestaltet werden; eine gänzlich andere Gruppe von Modultypen könnte entwickelt werden — der Fantasie sind wieder einmal keine Grenzen gesetzt. Und jeder ist seines eigener Schmied.
Ich jedenfalls finde die Modulbausteine, die ich vorgestellt habe, für meine eigene Arbeit sehr hilfreich, arbeits- und denkerleichternd. Es waren die folgenden (Bild 3).
ZENTRALMODUL
MODUL + NEBENMODUL
UNTERMODUL
PROZEDURENMODUL FUNKTIONENMODUL
DATENMODUL
INFORMATIONSMODUL
Strukturiertes Programmieren ist menschenfreundlich
Damit haben wir nunmehr alle Werkzeuge kennengelernt, die in dieser Serie als Hilfsmittel zum strukturierten Programmieren vorgestellt werden sollten. Diese Werkzeuge zu benutzen, damit zu programmieren, macht Spaß. Es macht immer Spaß, wenn man den Überblick behält, wenn man schnell vorankommt, wenn man am Ziel ankommen wird, das heißt wenn man sicher sein kann, daß man nicht irgendwo vorher im Schlamm komplexer und deshalb unlösbar scheinender Probleme steckenbleibt.
Natürlich macht man auch, wenn man strukturiert programmiert, Fehler — nicht nur einfache Tippfehler, sondern sogar Denkfehler — das ist normal und gehört zum kreativen Denken wie das »READY« auf dem Bildschirm. Aber solche Fehler lassen sich meist sofort nach dem Codieren eines Moduls (oder einer Prozedur oder einer Funktion) finden und beheben. Und wenn sich einmal ein Denkfehler wirklich erst dann zeigt, wenn das Programm eigentlich schon »fertig« ist, ist er auch dann noch schnell lokalisiert und ausgemerzt. Natürlicherweise kommen einem auch beim strukturierten Programmieren die besten Ideen erst dann, wenn es eigentlich schon »zu spät« ist. Auch kein Beinbruch, denn wenn ein Programm vernünftig durchstrukturiert ist, dann ist es nie wirklich zu spät. Verbesserungen betreffen ja immer Spezialaufgaben, und es ist ein Klacks, ein Modul oder eine Prozedur zu ergänzen oder eine neue Prozedur zu schreiben und einzufügen.
Strukturiertes Programmieren ist menschenfreundlich, es verringert den Programmieraufwand und macht das Programm lesbar, verstehbar und deshalb änderbar auch für den, der es nicht programmiert hat.
Aber ist strukturiertes Programmieren auch computerfreundlich?
Auf den ersten Blick scheint es, daß beides einander ausschließt. Denn wenn wir unser Programm laufen lassen, dann stellt sich heraus, daß es unerträglich langsam ist: Es gibt Antworten, auf die muß man mehr als 12 Sekunden lang warten. Und das ist sehr lang.
Wie kommt das? Das liegt erstens am Computer selbst, am C 128, dessen Basic-Version alles andere als ein Ausbund von Schnelligkeit ist. Das liegt zweitens aber am Basic an sich. Das Bestreben, in Basic strukturiert zu programmieren, führte ja dazu, daß ich eine große Menge an REM-Zeilen benutzen mußte und viele GOSUBs. Ich habe weiterhin viele Leerzeichen verwendet (für Einrückungen und zur Trennung der einzelnen Befehlswörter) sowie lange Variablennamen. Der Computer, wenn er das Programm abarbeitet, muß also viel lesen, was zwar für den Menschen relevant, für ihn aber völlig redundant ist. Das braucht Zeit. Viel Zeit.
Es gibt aber Lösungsmöglichkeiten. Den C 128 kann man auf FAST schalten, wenn man einen 80-Zeichen-Bildschirm sein eigen nennt. (Wer einen solchen nicht hat, aber sich nicht daran stört, wenn zwischen Frage und Antwort ständig der Bildschirm wegschnappt, kann FAST in Zeile 3015 einsetzen und SLOW in Zeile 4025 und dadurch die Wartezeit um die Hälfte verkürzen). Das strukturierte Programm kann man weiterhin compilieren, wenn man einen Compiler hat — der ignoriert alle REMs und alle Leerzeichen und beschleunigt auch sonst die Verarbeitung. Man kann schließlich eine Basic-Erweiterung wie Makrobasic benutzen, was die Wartezeit um bis zu 20 Prozent verkürzen kann.
Oder man greift gleich zu einer radikalen Lösung: Man nehme eine Programmiersprache, die von vornherein so konzipiert ist, daß sowohl der Mensch seine Freude hat, als auch der Computer. Eine solche Sprache ist — nein, nicht Pascal, da ist von Menschenfreundlichkeit wenig zu spüren. Nein, woran ich denke, ist Comal.
Ich habe unser C 128-Basic-Programm umgewandelt in ein C 64-Comal-Programm. Ich habe es nicht etwa neu geschrieben, sondern (mit einem Trick) von der Diskette direkt in Comal eingelesen und dann die notwendigen Anpassungen vorgenommen. Das Comal-Programm ist kürzer (statt 20174 nur 16433 Byte); es ist 5- bis 6mal so schnell, je nach der eingegebenen Frage (wo ich bei Basic 12,28 Sekunden auf eine bestimmte Antwort warten mußte, sind es jetzt nur noch 2,4 Sekunden). Das Programm könnte noch etwas schneller und kürzer sein, wenn ich es direkt in Comal geschrieben und nicht »wörtlich« aus Basic übersetzt hätte.
Menschenfreundliches und computerfreundliches Programmieren schließen einander also nicht prinzipiell aus. Comal beweist es. Den Namen Comal sollte man sich merken.
Wir waren einmal von der Frage ausgegangen, ob man auch in Basic strukturiert programmieren könne. Die Antwort war: Natürlich kann man.
Aber soll man auch? Die Antwort muß wiederum lauten: Natürlich soll man! Was für eine vernünftige Alternative gäbe es denn? Gut, es treten Probleme auf, vor allem, wenn ein Programm eine gewisse Länge überschreitet. Aber diese lassen sich lösen. Vorschläge dazu wurden gemacht. Die Alternative zum strukturierten Programmieren in Basic ist jedenfalls nicht unstrukturiertes Programmieren.
(Prof. Burkhard Leuschner/nj)Hinweise zu »Comalchen«
Der Zweck des Programms COMALCHEN ist es, Fragen über Comal zu beantworten. Fragen können die Form von W-Fragen haben (Was ist..., Wieviel kostet..., Wer hat...) oder von Ja-Nein-Fragen (Ist..., kann..., bekommt man...).
Beobachtungen bei der Benutzung der ursprünglichen Version von COMALCHEN hatten gezeigt, daß die Benutzer selten beim Thema bleiben, entweder aus Vergeßlichkeit oder einfach aus Mutwillen.
Es ist also notwendig, nicht nur Eingaben zu berücksichtigen, die sich tatsächlich auf Comal beziehen, sondern (vor allem!) Eingaben, die sich nicht auf Comal beziehen. Daraus ergeben sich vier Typen von Eingaben:
1. W-Fragen, 2. Ja-Nein-Fragen, 3. Eingaben mit »du«,
4. Sonstige Eingaben: Aussagen (Nicht-Fragen)
Die Unterscheidung dieser Typen führt das Programm mit sehr einfachen Mitteln durch. Eingaben mit Fragezeichen am Ende sind Fragen. Wenn ein Fragewort enthalten ist, dann sind es W-Fragen, anderenfalls Ja-Nein-Fragen. Alles andere sind zunächst Aussagen. Wenn »du« enthalten ist, dann handelt es sich um eine Eingabe mit »du«, also um den dritten Typ. Alles andere gilt als Aussage des Typs 4. Die Antworttypen bestimmen sich nach mehreren Gesichtspunkten:
Typ 1. nach dem Fragetyp
Typ 2. nach der erfragten Information
Typ 3. danach, ob der Computer mit der Frage etwas anfangen konnte, ob er sie »verstand«
Typ 4. nach der Fragedisziplin des Benutzers
Die Antwortmöglichkeiten sind vom Programm vorgegeben. Sie können vom Computer nicht »erfunden«, sondern nur ausgewählt werden. Wo mir mehrere Antwortmöglichkeiten auf eine Frage einfielen, kann der Computer aus mehreren Antworten auswählen. Er tut dies nach dem Zufallsverfahren, achtet aber darauf, daß er erst alle Möglichkeiten erschöpft hat, ehe er eine schon einmal gegebene Antwort wiederholt. Die Antworten werden so gegeben, wie sie vorgegeben sind, mit einer Ausnahme: Manche Antworten sind »Rahmen«; der Rahmen ist vorgegeben, die Füllung des Rahmens hängt von der jeweiligen Eingabe ab, zum Beispiel »Was meinst du mit...?«
Die Auswahl einer Antwort geht so vor sich:
Auswahl nach dem Fragewort (Typ 1)
Jedem Fragetyp entspricht zunächst ein Antworttyp, das heißt W-Fragen werden mit Fakten beantwortet, Ja-Nein-Fragen mit »Ja«, »Nein«, »Zweifellos« oder ähnliche Eingaben, die »du« enthalten werden abgewehrt, sonstige Aussagen je nach Aussagetyp behandelt, »Adieu« führt zum Beispiel zur Beendigung des Programms.
Auswahl nach der erfragten Information, also nach dem Inhalt der Frage (Typ 2)
Wenn der Computer eine Frage überprüft, versucht er herauszufinden, worüber der Fragende etwas wissen will. Dazu sucht er nach »Schlüsselwörtern« in der Eingabe. Wenn zum Beispiel das Wort »Preis« enthalten ist, dann ist die Wahrscheinlickeit groß, daß der Benutzer wissen will, was COMAL kostet.
Wenn der Computer also eines dieser Schlüsselwörter findet, dann nimmt er an, daß die Frage den Preis betrifft. Er speichert dann »Preis« als Oberbegriff.
Es gibt im Datenmodul, getrennt durch Fragetypen, Gruppen solcher Oberbegriffe mit zugehörigen Schlüsselwörtern.
Wenn der Computer nun eine passende Antwort sucht, dann begibt er sich zunächst zu dem entsprechenden Antworttyp, zum Beispiel den Faktenantworten. Dort sucht er nach dem gespeicherten Oberbegriff, also zum Beispiel »Preis«. Aus den zugehörigen Antwortmöglichkeiten wählt er dann eine aus.
Der Computer versteht die Frage nicht (Typ 3)
Wenn der Computer keinen Oberbegriff gefunden hatte, kann er mit der Eingabe nichts anfangen. Wenn er eine Frage nicht verstanden hat, antwortet er mit »Ich weiß nicht« (oder einer alternativen Möglichkeit); wenn er eine Aussage nicht verstanden hat, sagt er: »Ich habe es nicht verstanden«.
Der Benutzer verirrt sich (Typ 4)
Manche Benutzer vergessen völlig, was das Thema ist. Um den Fragenden wieder auf die richtige Fährte zu bringen, wird gezählt, wie oft eine Eingabe nicht verstanden wurde. Nach dem dritten Mal wird an das Thema erinnert. Wenn der Benutzer trotzdem weiter das Thema verfehlt, bricht der Computer nach dem sechsten Mal die Unterhaltung ab.
Wie »natürlich« die Unterhaltung wirkt, hängt von der Logik des Fragers ab, aber auch von den Daten, das heißt den Schlüsselwörtern und den zugeordneten Antwortmöglichkeiten. Da kann noch unendlich viel verbessert und ergänzt werden. Normal ist, daß einem bei jedem Programmlauf eine neue Idee einfällt. Versuchen Sie zum Beispiel, eine Hilfe für denjenigen Benutzer einzubauen, der nicht mehr weiß, worüber er Fragen stellen könnte.
Falls Sie eigene Daten eingeben wollen — die Zahlen hinter den Oberbegriffen der Antwortdaten dienen dazu, die Liste zu führen, die registriert, welche Antwortmöglichkeiten jeweils schon »verbraucht« sind. Diese Zahlen brauchen nicht in numerischer Reihenfolge zu sein. Wenn Sie irgendwo eine neue Frage plus Antwortgruppe einfügen, geben Sie ihr einfach die nächste laufende Nummer. Hauptsache, jede Antwortgruppe hat ihre eigene Zahl. Achten Sie bei der Dimensionierung des Feldes LISTE$( ) darauf, daß das Feld auch groß genug ist.
Natürlich können Sie alle meine Daten hinauswerfen und eigene eingeben. Achten Sie aber bitte darauf, daß die Grundgruppierungen mit den bisherigen Zeilennummern erhalten bleiben, denn darauf springt das Programm mit RESTORE zu.
10 rem programm: comalchen
20 rem version: 1.1
25 rem datum: 27.2.86
30 rem computer: c128
40 rem sprache: basic 7.0
50 rem autor: burkhard leuschner
60 :
100 rem *******************************
110 rem zentralmodul
130 : gosub 1000 vorbereitung
140 : gosub 2000 begruessung
150 :
160 : do
170 : gosub 3000 frage
180 : gosub 4000 antwort
190 : loop until adieu
200 :
210 : gosub 5000 abschied
220 : gosub 6000 programmende
240 rem ende zentralmodul
250 rem *******************************
260 :
270 :
1000 rem modul vorbereitung
1010 : gosub 50000 system konfiguration
1020 : gosub 51000 initialisierungen
1030 : gosub 46000 funktionen
1040 : gosub 52000 daten lesen
1050 return
1060 rem ende modul vorbereitung
1070 :
2000 rem modul begruessung
2010 : gosub 10000 vorstellung
2020 : gosub 10200 begruessen
2030 : gosub 10500 beginn unterhaltung
2040 return
2050 rem ende modul begruessung
2060 :
3000 rem modul frage
3010 : gosub 11000 eingabe frage
3015 rem fast
3020 : gosub 11300 fragentyp feststellen
3030 : gosub 11500 schluesselwort suchen
3040 return
3050 ende modul frage
3060 :
4000 rem modul antwort
4010 : gosub 15000 anworttyp festlegen
4020 : gosub 15400 antwort waehlen
4025 rem slow
4030 : gosub 15600 antwort ausgeben
4040 return
4050 rem ende modul antwort
4060 :
4070 :
5000 rem modul abschied
5020 : gosub 29000 abschiedskommentar
5030 return
5040 rem ende modul abschied
5050 :
6000 rem modul programmende
6005 : printchr$(12)
6010 : end
6020 ende modul programmende
6030 :
6040 rem ******************************
6050 rem ******************************
6060 :
10000 rem nebenmodul begruessung
10010 rem ----------------------
10020 rem u-modul vorstellung
10025 rem export: name$
10030 : print chr$(147)
10040 : print "Ich bin Comalchen - und wer bist du?
10050 : print
10060 : do
10070 : print
10080 : input name$
10090 : print "Ist ";name$;" dein Vorname?"
10100 : input mjn$
10110 : if instr("nN",left$(mjn$,1)) then begin
10120 : print
10130 : print"Ich moecht' aber doch gern deinen Vornamen wissen!"
10133 : print
10135 : print"Wie heisst du also?"
10140 : bend
10150 : loop until instr("jJ",left$(mjn$,1))
10160 return
10170 rem ende u-modul vorstellung
10180 :
10200 rem u-modul begruessen
10210 rem import: name$
10220 : do
10230 : print
10240 : print"Gruess Gott, ";name$;"."
10250 : print
10260 : input meingabe$
10270 : ueingabe$=meingabe$
10280 : gosub 40200 lowercase
10290 : gosub 40400 sonderzeichen
10300 : meingabe$=ueingabe$
10310 : restore 55830:rem gruss
10320 : do
10330 : read mgruss$
10340 : if not(mgruss$="&") then begin
10350 : if instr(meingabe$,mgruss$) then mok=true
10355 : bend
10360 : loop until mok or mgruss$="&"
10370 : loop until mok
10380 return
10390 rem ende u-modul begruessen
10400 :
10500 rem u-modul beginn unterhaltung
10510 : print
10520 : print"Du willst dich also mit mir ueber COMAL unterhalten.
10530 : print
10540 : print"Ok. Schiess los. Was willst du wissen?
10550 : print
10670 return
10680 ende u-modul beginn unterhaltung
10700 rem ende nebenmodul begruessung
10710 :
10720 :
11000 rem nebenmodul frage
11010 rem ----------------
11020 rem u-modul eingabe frage
11030 rem export: frage$,eingabe$
11040 : gosub 12000 eingabe
11050 : eingabe$=frage$
11060 : ueingabe$=eingabe$
11070 : gosub 12500 form standardisieren
11080 : eingabe$=ueingabe$
11090 return
11100 rem ende u-modul eingabe frage
11110 :
11300 rem u-modul fragentyp feststellen
11310 rem import: eingabe$
11320 rem export: tpeingabe
11330 : tpeingabe=0
11340 : if right$(ei$,1)="?" then begin
11350 : gosub 13000 fragepronomen
11360 : if mgefunden then tpeingabe=1 : else tpeingabe=2
11370 : bend: else tpeingabe=4
11380 : if tp=4 then if fn du drin(0) then tp=3
11390 return
11400 rem ende u-modul fragetyp feststellen
11410 :
11500 rem u-modul schluesselwort suchen
11510 rem import: tpeingabe
11520 rem export: oberbegriff$,ky
11530 : gosub 14000 schluesselgruppe suchen
11540 : gosub 14500 oberbegriff finden
11550 return
11560 rem ende u-modul schluesselwort suchen
11570 :
12000 rem proc: eingabe
12010 rem export: frage$
12020 : do
12025 : frage$=""
12027 : rem poke 208,3:poke ?,34:poke ?+1,34:poke ?+2,20: rem tastaturpuffer
12028 : rem soll anfuehrungszeichen beim input erzeugen
12030 : input frage$
12040 : if frage$="" then print"Hast du was gesagt?"
12050 : loop until frage$>""
12060 return
12070 :
12500 rem proc: form standardisieren (ueingabe$:in/out)
12510 : gosub 40000 leerzeichen
12520 : gosub 40200 lowercase
12530 : gosub 40400 sonderzeichen
12540 return
12550 :
13000 rem proc: fragepronomen
13010 rem import: eingabe$, pronomen$(),zahlpronomen
13020 rem export: mgefunden,wh
13030 : mgefunden=false:wh=false
13040 : for ui=1 to zahl
13050 : if right$(eingabe$,len(pr$(ui))+1)=pr$(ui)+"?" then wh=true
13060 : if instr(ei$,pr$(ui)) then mg=true: ui=zahl
13070 : next
13080 return
13090 :
14000 rem proc schluesselgruppe suchen
14010 rem import: tpeingabe
14020 : rem case
14030 : on tpeingabe goto 14040,14060,14080,14100:goto 14120
14040 : restore 55000:rem w-fragen
14050 : goto 14120
14060 : restore 55400:rem j/n-fragen
14070 : goto 14120
14080 : restore 55600:rem du
14090 : goto 14120
14100 : restore 55800:rem aussage
14110 : goto 14120
14120 : rem endcase
14130 return
14140 :
14500 rem proc: oberbegriff finden
14510 rem import: eingabe$
14520 rem export: oberbegriff$,ky
14530 : do
14540 : read oberbegri ff$
14550 : if not(ob$="@@@")then begin
14560 : do
14570 : read uschluessl$
14580 : if not(us$="&") then begin
14590 : up os=instr(us$,"&")
14600 : if up=0 then ky=(instr(eingabe$,us$)<>0):else begin
14610 : uwo rt$(1)=left$(us$,up-1)
14620 : uwo rt$(2)=mid$(us$,up+1)
14630 : ky=(instr(ei$,uw$(1)) and instr(ei$,uw$(2))<>0)
14640 : bend
14650 : bend
14660 : loop until ky or us$="&"
14670 : bend
14680 : loop until ky or ob$="@@@"
14690 return
14700 rem ende nebenmodul frage
14710 :
14720 :
15000 rem nebenmodul antwort
15010 rem ------------------
15020 rem u-modul antworttyp festlegen
15030 rem import: tpeingabe,ky
15040 rem export: typantwort,oberbegriff$
15050 : rem case
15060 : on tpeingabe goto 15070,15110,15130,15150:goto15160
15070 : if wh then ky=0:ob$="nur fragepronomen":typantw=1:ni=0:else begin
15080 : if ky then typ=1:nichtthema=0:else typ=5:ni=ni+1:ob$="weissnicht"
15090 : bend
15100 : goto 15160
15110 : if ky then typ=2:ni=0:else typ=5:ni=ni+1:oberbegri ff$="weissnicht
15120 : goto 15160
15130 : typ=3:ni=ni+1
15140 : goto 15160
15150 : if ky then typ=4:ni=0:else typ=6:ni=ni+1:ob$="verstehnicht"
15160 : rem endcase
15170 :
15180 : if ni>6 then typantw=8:oberbegr$="abbruch":adieu=true:else begin
15190 : if nichtthema>3 then typantw=7:oberbegri ff$="themaverfehlt"
15200 : bend
15210 : if ob$="abschied" then adieu=true
15220 return
15230 rem ende u-modul antworttyp festlegen
15240 :
15400 rem u-modul antwort waehlen
15410 rem import: typantwort
15415 rem export: antwort
15420 : gosub 16000 antwortgruppe finden
15430 : gosub 43000 antwort aussuchen
15440 return
15450 rem ende u-modul antwort waehlen
15460 :
15600 rem u-modul antwort ausgeben
15610 rem import: antwort$
15620 : gosub 44000 anwort ergaenzen
15630 : gosub 45000 antwort auf bildschirm
15640 return
15650 rem ende u-modul antwort ausgeben
15660 :
16000 rem proc antwortgruppe finden
16010 rem import: typantwort
16020 : rem case
16030 : on typantw goto 16050,16070,16090,16110,16130,16150,16170,16190
16040 : goto16200
16050 : restore 56000:rem faktenantw
16060 : goto 16200
16070 : restore 56500:rem j/n-antw
16080 : goto 16200
16090 : restore 56700:rem du
16100 : goto 16200
16110 : restore 56800:rem aussage
16120 : goto 16200
16130 : restore 57000:rem weiss nicht
16140 : goto 16200
16150 : restore 57300:rem versteh nicht
16160 : goto 16200
16170 : restore 57500:rem thema verfehlt
16180 : goto 16200
16190 : restore 57800:rem abbruch
16200 : rem endcase
16220 return
16240 rem ende nebenmodul antwort
16250 :
16260 :
29000 rem nebenmodul abschied
29010 rem -------------------
29020 rem u-modul abschiedskommentar
29030 : restore 57900
29040 : oberbegri ff$="ende"
29050 : gosub 43000 antwort aussuchen
29060 : gosub 44000 antwort ergaenzen
29070 : gosub 45000 antwort auf bildschirm
29080 return
29090 rem ende u-modul abschiedskommentar
29110 ende nebenmodul abschied
29120 :
29130 :
40000 rem prozedurenmodul
40010 rem ===============
40020 rem proc leerzeichen (ueingabe$: in/out)
40030 : do while left$(ueingabe$,1)=" ":ue$=mid$(ue$,2):loop
40040 : do while right$(ue$,1)=" ":ue$=left$(ue$,len(ue$)-1):loop
40050 : do while instr(ue$," ")
40060 : up=instr(ue$," ")
40070 : ue$=left$(ue$,up-1)+mid$(ue$,up+1)
40080 : loop
40090 return
40100 :
40200 rem proc: lowercase (ueingabe$: in/out)
40205 rem import klein$,gross$
40210 : for ui=1 to len(ue$)
40220 : if instr(gr$,mid$(ue$,ui,1)) then begin
40230 : up=instr(gr$,mid$(ue$,ui,1))
40240 : ue$=left$(ue$,ui-1)+mid$(kl$,up,1)+mid$(ue$,ui+1)
40250 : bend
40260 : next
40270 return
40280 :
40400 rem proc: sonderzeichen (ueingabe$: in/out)
40410 : uumlaute$ ="{CBM-V}{CBM-F}{CBM-C}{CBM-X}"
40420 : usubstitutes$="ssaeoeue"
40430 : for ui=1 to len(uum$)
40440 : uzeichen$=mid$(uum$,ui,1)
40450 : do while instr(ue$,uz$)
40460 : up=instr(ue$,uz$)
40470 : ue$=left$(ue$,up-1)+mid$(usub$,ui*2-1,2)+mid$(ue$,up+1)
40480 : loop
40490 : next
40500 return
40510 :
43000 rem proc antwort aussuchen
43010 rem import: oberbegriff$,liste$(),null$
43020 rem export: antwort$,liste$()
43030 : uzaehler=0
43040 : do
43050 : read utitel$
43060 : if not(ut$="@@@") then begin
43070 : if ut$=ob$ then begin
43080 : read unr lis te
43100 : do
43110 : uz=uz+1
43120 : read uantw$(uz)
43130 : loop until uantw$(uz)="&"
43140 : uz=uz-1
43150 : bend
43155 : bend
43160 : loop until uantw$(uz+1)="&" or ut$="@@@"
43180 :
43190 : if instr(lis te$(unr),"0")=0 then li$(unr)=left$(null$,uz)
43210 :
43220 : do
43230 : up=fn zufall(uz)
43240 : loop until mid$(li$(unr),up,1)="0"
43250 :
43260 : antwo rt$=uantw$(up)
43270 : li$(unr)=left$(li$(unr),up-1)+"1"+mid$(li$(unr),up+1)
43280 return
43290 :
44000 rem proc antwort ergaenzen
44010 rem import: antwort$,frage$,name$
44020 rem export: antwort$
44030 : if instr(antw$,"+") then begin
44040 : up=instr(an$,"+name")
44050 : if up then an$=left$(an$,up-1)+name$+mid$(an$,up+5)
44060 : up=instr(an$,"+frage")
44070 : if up then an$=left$(an$,up-1)+frage$+mid$(an$,up+6)
44080 : bend
44090 return
44100 :
45000 rem proc: antwort auf bildschirm
45010 rem import: antwort$
45020 : print " ";antwo rt$
45030 : print
45040 return
45060 rem ende prozedurenmodul
45070 :
45080 :
46000 rem funktionenmodul
46010 rem ===============
46020 rem func du drin
46025 : rem import: eingabe$
46030 : def fn du(x)=abs(instr(ei$,"du ")>0 or instr(ei$," du")>0)
46040 rem endfunc
46050 :
46060 rem func zufallszahl(obergrenze:in)
46070 : udummy=rnd(-ti)
46080 : def fn zufall(ob)=int(rnd(1)*ob)+1
46090 rem endfunc
46100 :
46110 return
46120 rem ende funktionenmodul
46130 :
46140 :
50000 rem nebenmodul vorbereitung
50010 rem -----------------------
50020 rem u-modul system konfiguration
50030 : print chr$(14):printchr$(11)
50040 : color 0,9:color 4,9:color 5,2
50090 return
50100 rem ende u-modul system konfiguration
50110 :
51000 rem u-modul initialisierungen
51010 : null$="00000000000000000000"
51020 : gross$="ABCDEFGHIJKLMNOPQRSTUVWXYZ{SHIFT-+}{CBM--}{SHIFT--}"
51030 : klein$="abcdefghijklmnopqrstuvwxyy{CBM-F}{CBM-C}{CBM-X}"
51100 : true=1:false=0
51110 : adieu=false
51120 : zahlpro nomen=13
51200 : dim pro nomen$(zahl),lis te$(40), uantwo rt$(20)
51970 return
51980 rem ende u-modul initialisierungen
51990 :
52000 rem u-modul daten lesen
52010 : restore 58900: rem fragepronomen
52020 : for ui=1 to zahl: read pro nomen$(ui):next
52030 return
52040 rem ende u-modul daten lesen
52050 rem ende nebenmodul vorbereitung
52060 :
53990 :
55000 rem datenmodul
55005 rem ::::::::::
55010 rem schluesselwoerter
55020 rem -----------------
55030 rem 1: w-fragen
55040 data thema
55045 data thema,worueber,&
55050 data programmiersprache
55060 data was&programmiersprache,&
55080 data alter
55090 data wie alt,wann,&
55100 data erfinder
55110 data wer&erfunden,wem&erfunden,wem&entwickelt,wer&entwickelt,erfind,&
55120 data qualitaet
55130 data gut,eigenschaft,besonder,vorteil,schnell,&
55140 data herkunft
55150 data woher,wo&her,land,&
55160 data literatur
55170 data literatur,info,lesen,erfahr,buch,buech,&
55180 data preis
55190 data kost,preis,wieviel,teuer,&
55200 data quelle
55210 data bezieh,bezugs,bezog,bekomm,krieg,kauf,von wem,&
55220 data comalgruppen
55230 data gruppe,adress,user,group,&
55240 data implementierung
55250 data computer,maschin,system,&
55260 data versionen
55270 data version,&
55280 data name
55290 data heiss&comal,bedeutet&comal,&
55300 data strukturiert
55310 data strukturiert,programmieren,programme,befehl,&
55320 data warum nicht
55330 data warum&nicht,&
55340 data nichtverstanden
55350 data wie&bitte,was&meinst,was&sag,,soll&heiss
55351 data &
55355 data definition
55356 data was&comal,&
55360 data @@@
55370 :
55400 rem 2: ja/nein-fragen
55410 data qualitaet
55420 data gut,besser&basic,besser&pascal,vorteil,&
55430 data literatur
55440 data literatur,info,lesen,erfahr,buch,buech,artikel,&
55450 data preis
55460 data preis,viel,teuer,&
55470 data kauf
55480 data kauf,schenk,&
55490 data versionen
55500 data disk,kasset,cass,cc,version,modul,rom,"ram "," ram",&
55510 data strukturiert
55520 data struktur,&
55530 rem nachfrage
55540 data meinstdu
55550 data meinst&du,glaubst&du,sicher,wirklich,&
55555 data comalgruppen
55556 data gruppe,adress,user,group,&
55560 data @@@
55570 :
55600 rem 3: du
55610 data boese
55620 data was&weisst,du&weisst,!,&
55630 data du
55640 data du,&
55650 data @@@
55660 :
55800 rem 4: aussage
55810 data abschied
55820 data ade,adieu,wiederseh,ende,aufhoer,bis&spaet,tschues,tschau,&
55830 data gruss
55840 data tag, morgen,abend,hallo,gruess,gott,gruss,&
55850 data ausruf
55860 data !,&
55870 data ja
55880 data ja,mhm,ich&weiss,&
55890 data nein
55900 data nein,nicht&versteh,nicht&verstand,&
55910 data dank
55920 data dank,thank,merci,&
55970 data @@@
55980 :
56000 rem antworten
56005 rem ---------
56010 rem 1: faktenantworten
56015 data definition,1
56020 data "COMAL ist eine Programmiersprache."
56025 data &
56030 data programmiersprache,2
56035 data"Eine Programmiersprache dient dazu, ei-nen Computer zu programmieren.
56040 data &
56045 data alter,3
56050 data "COMAL ist keineswegs neu."
56055 data "COMAL ist schon 13 Jahre alt."
56060 data "COMAL wurde schon 1973 entwickelt."
56065 data &
56070 data erfinder,4
56075 data "Der 'Erfinder' von COMAL ist Borge Christensen."
56080 data "COMAL wurde von B. Christensen und B. Loefstedt entwickelt."
56085 data &
56090 data qualitaet,5
56095 data "COMAL ist eindeutig die Programmier- sprache der Zukunft."
56100 data "COMAL ist so gut wie BASIC und PASCAL zusammen."
56105 data "COMAL hat die guten Eigenschaften von BASIC und PASCAL."
56110 data "Wenn du COMAL hast, dann kannst du BASIC vergessen."
56115 data "Andere Sprachen wie FORTH, ELAN, PASCALverblassen vor COMAL."
56120 data "COMAL ist eine Sprache, die v.a. auch den Menschen ernst nimmt."
56125 data "COMAL ist z.B. menschenfreundlich."
56130 data &
56135 data herkunft,6
56140 data "COMAL kommt aus Daenemark."
56145 data "Die Erfinder von COMAL sind Daenen."
56150 data "COMAL kommt, wie auch andere gute Soft- ware, aus Europa."
56155 data &
56160 data literatur,7
56165 data "Es gibt eine ganze Menge Literatur, v.a. auf Englisch."
56170 data "Der Erfinder von COMAL hat mehrere Buecher geschrieben."
56175 data "Heiko Wolgast uebersetzte z.B. Christensens 'C. from A-Z'"
56180 data "Das COMAL 0.14 Handbuch, z.B.
56185 data"Fuer Anfaenger: Christensen, 'Struktu- rierte Programmierung mit C.'"
56190 data "Alles ueber COMAL enthaelt das 'COMAL Handbook' von Len Lindsay."
56195 data "Vera Birkenbihl schrieb 1964 das Buch 'Von Basic zu COMAL'."
56200 data "1986 erscheint von Volker Fischer das Buch 'COMAL in Beispielen'."
56205 data "Im '64er' findet man seit August 1984 gelegentlich Informationen.
56210 data &
56215 data preis,8
56220 data"COMAL 0.14 kostet fast nichts, das Steckmodul (V. 2.0) DM 198."
56225 data "Das Steckmodul mit COMAL 2.01 kostet DM 198.-, 0.14 ein paar Mark."
56230 data &
56235 data quelle,9
56240 data "Bezugsquellen sind v.a. die beiden deutschen COMAL Gruppen.
56245 data &
56250 data comalgruppen,10
56255 data "Chr. Canisius, Freiheitstr. 30, 4000 D'd 12 / D. Belz, 2270 Utersum
56260 data &
56265 data implementierung,11
56270 data "COMAL gibt's v.a. fuer Commodore Computer."
56275 data "COMAL gibt's auch fuer den IBM PC und Kompatible."
56280 data "Ab Fruehjahr gibt's COMAL fuer 2.0 fast alle gaengigen Computer.
56285 data &
56290 data versionen,12
56295 data "Die neueste Version ist 2.0 (z.B.als Steckmodul fuer den C64)."
56300 data "Die aelteste Version ist COMAL 0.11 (1981 fuer den PET)."
56305 data "Die preiswerteste Version ist im Augen- blick die 0.14 (C64)."
56310 data &
56315 data name,13
56320 data "'COMAL' bedeutet 'Common Algorithmic Language'."
56325 data "Was der Name bedeutet, darauf kommt's eigentlich gar nicht an."
56330 data "Wichtig ist, was COMAL tut, nicht was der Name meint."
56335 data "'COMAL' ist eine Abkuerzung. aber die macht nicht viel Sinn."
56340 data &
56345 data strukturiert,14
56350 data "So programmieren, dass auch ein Mensch das Programm versteht."
56355 data "Uebersichtlich programmieren.
56360 data "Beim Programmieren auch an den Menschendenken.
56365 data &
56370 data warum nicht,15
56375 data "Weil!","Nun ja ...","Das weisst du selber sehr wohl."
56380 data &
56385 data nichtverstanden,16
56390 data "War ich so unklar?","Du hast mich nicht verstanden?"
56395 data "Hab ich mich so undeutlich aus- gedrueckt?"
56400 data "Also, deutlicher kann ich's nicht mehr sagen."
56405 data &
56410 data nur fragepronomen,17
56415 data "+frage{left} was?","+frage","Was meinst du mit '+frage{left}'?"
56420 data "Schwierig, schwierig ..."
56425 data "Als Computer habe ich Probleme, solche Kurzfragen zu verstehen."
56430 data &
56435 data thema,18
56440 data "COMAL natuerlich.","Wie bisher: COMAL."
56445 data "Immer noch COMAL.","Glaub's oder glaub's nicht: COMAL!!!"
56450 data &
56485 data @@@
56490 :
56500 rem 2: ja/nein-antworten
56505 data strukturiert,19
56510 data "Wer strukturiert programmieren will,derist mit COMAL gut bedient.
56515 data "Wenn du struktruriert denkst, hilft COMAL beim Kodieren."
56520 data "COMAL hat Prozeduren und Funktionen, umnur einiges zu nennen."
56525 data "COMAL hat Repeat-, While- und For- schleifen, z.B."
56530 data "COMAL stellt u.a. If- und Case-Bloecke zur Verfuegung."
56535 data "Mit COMAL strukturiert zu programmieren ist ein Gedicht."
56540 data &
56545 data qualitaet,20
56550 data "Ja.","Eindeutig.","Ohne Zweifel."
56555 data &
56560 data literatur,21
56565 data "Ja, eine ganze Menge, und nicht nur aufEnglisch, auch auf Deutsch.
56570 data &
56575 data preis,22
56580 data "Kann man eigentlich nicht sagen.","Nicht recht eigentlich."
56585 data &
56590 data kauf,23
56595 data "Die Version 0.14 bekommt man fast ge- schenkt, die andern kosten.
56600 data &
56605 data versionen,24
56610 data "Es gibt Versionen auf Diskette und im ROM (z.B. C64-Steckmodul)."
56615 data &
56620 rem bestaetigung auf nachfrage
56625 data meinstdu,25
56630 data "Ich denk schon.","Warum nicht?","Was sollte dagegen sprechen?"
56635 data "Gewiss doch.","Aber ja doch."
56640 data &
56642 data comalgruppen,26
56643 data "Chr. Canisius, Freiheitstr. 30, 4000 D'd 12 / D. Belz, 2270 Utersum
56644 data &
56645 data @@@
56650 :
56700 rem 3: 'du'-antwort
56705 data du,27
56710 data "Ich bin voellig unwichtig."
56715 data "Mich kannst du gern vergessen."
56720 data "Ich bin doch nicht das Thema."
56725 data "Lass ruhig mich aus dem Spiel."
56730 data "Wir wollten doch nicht ueber mich reden."
56735 data &
56740 :
56745 data boese,28
56750 data "Aber, ich bitte dich!","Reiss dich zusammen!"
56755 data "Meinst du doch nicht im Ernst, oder?"
56760 data "So was sagt man doch nicht!!!"
56765 data "Was ficht dich ploetzlich an?"
56770 data &
56775 data @@@
56780 :
56800 rem 4: aussagen
56810 data gruss,29
56820 data "Ich fuerchte, du hast vorhin geschlafen."
56830 data "Haben wir uns nicht schon begruesst?"
56840 data "Ich sag gern auch ein zweites Mal 'Guten Tag'!"
56850 data &
56860 data abschied,30
56870 data "Wiedersehen, +name.","Ade.", "Tschuessle.","Adieu."
56880 data "Du gehst schon? Na dann, Auf Wieder- sehen, +name."
56890 data &
56900 data ausruf,31
56910 data "Nimm's nicht so schwer.","Es sieht schlimmer aus, als es ist!"
56920 data "Ist alles halb so schlimm.","Nur Mut!"
56930 data "Keine Angst! Du wirst es schaffen.","Nimm's nicht so tragisch."
56940 data &
56950 data ja,32
56955 data "Ach so.","Nun ja ...","Gut.","Schoen.","Dann bin ich beruhigt."
56960 data &
56965 data nein,33
56970 data "Ach so?","Nun denn...","Nein?","Schad drum.","Wie du willst!"
56975 data "Kann man nichts machen.","Na ja, was soll's ..."
56980 data &
56982 data dank,39
56983 data "Bitte.","Bitte sehr.","Aber fuer dich tu ich doch alles."
56984 data "Bitte schoen.","Ich bin dir immer gern behiflich."
56985 data &
56990 data @@@
56995 :
57000 rem 5: weiss nicht
57010 data weissnicht,34
57020 data "Keine Ahnung.","Darueber weiss ich leider nichts."
57030 data "Da bin ich ueberfragt.","Da musst du schon einen Experten fragen."
57040 data "Ich weiss eine ganze Menge, aber das leider nicht, +name."
57050 data "Davon hab ich nie was gehoert."
57060 data "Tut mir leid, weiss ich nicht."
57065 data "Wenn ich das wuesste ...","Das ist eine gute Frage, bloss ...
57070 data &
57080 data @@@
57090 :
57300 rem 6: versteh nicht
57310 data verstehnicht,35
57320 data "Wie bitte?","Was hast du gesagt?"
57330 data "Verzeihung, ich hab wohl{rght}grad geschlafen."
57340 data "Koenntest du das noch mal in andern Worten sagen?"
57350 data "Ich versteh immer nur Bahnhof..."
57360 data "Was meinst du damit?"
57370 data "Vielleicht solltest du dich einfacher ausdruecken."
57380 data "Ich versteh nicht ganz - koenntest du das noch einmal anders sagen?
57390 data "Was meinst du mit: '+frage'???"
57400 data "Was heisst '+frage'???"
57410 data "'+frage' - das versteh ich nicht."
57420 data &
57430 data @@@
57440 :
57500 rem 7: thema verfehlt
57510 data themaverfehlt,36
57520 data "Weisst du ueberhaupt noch, worueber du redest?"
57530 data "Hast wohl das Thema vergessen!?"
57540 data "Wenn ich mich recht erinnere, wollten wir ueber COMAL reden!
57550 data "COMAL war unser Thema, vergessen?"
57560 data "Worueber redest du eigentlich?"
57570 data "Vergiss das Thema nicht!"
57580 data "COMAL ist unser Thema!"
57590 data "Bitte lass uns beim Thema bleiben."
57600 data &
57610 data @@@
57620 :
57800 rem 8: abbrechen
57810 data abbruch,37
57820 data "Ich glaube, es hat keinen Sinn, mit dirueber COMAL zu reden.
57830 data "Wir verstehen uns heute offenbar nicht.
57840 data "Es hat keinen Zweck mit uns beiden!
57850 data &
57860 data @@@
57870 :
57900 rem abschiedskommmentar
57905 data ende,38
57910 data "Ich hab mich gern mit dir unterhalten, +name."
57915 data "Hat mich gefreut, dich kennenzulernen."
57920 data "Besuch mich mal wieder, +name."
57925 data "Ich haett mich gern noch laenger mit dir unterhalten."
57930 data "Schad, dass du schon gehst, +name."
57935 data &
57940 data @@@
57950 :
57955 :
58900 rem fragepronomen
58910 data wer,was,wann,warum,wieso,wie,wem,wen,welch,wessen,woher,wo,&
58980 :
58990 rem ende datenmodul
58995 :
58996 :
60000 rem informationsmodul
60003 rem .................
60005 rem frage- und antworttypen
60007 :
60010 rem nr eingabetyp antworttyp
60015 rem .............................
60020 rem 1: w-frage faktenantwort
60030 rem 2: ja/nein-fr ja/nein-antw
60040 rem 3: enth 'du' 'du'-antwort
60050 rem 4: aussage aussage
60070 rem 5: weiss nicht
60080 rem 6: versteh nicht
60090 rem 7: thema verfehlt
60100 rem 8: abbruch