Dieses Kapitel gehoert zum Kurs Programmieren in Oberon des Fachbereichs Informatik.

Zaehlmal -- erste Ausbauetappe

Bearbeiten

Also gut, das zweite Programm läuft und zählt auf 10, Friede, Freude, Eierkuchen. Beim ersten mal machts Spass, beim zweiten vielleicht auch immer noch ein wenig, beim dritten nur noch ein bisschen Stolz, aber ihr merkt ja, so spannend ist das Ding nicht. Was ist, wenn wir uns nicht mehr mit 10 zufrieden geben, sondern mehr, sagen wir 11, wollen? Naja, wir könnten das Programm verändern, abspeichern, neu compilieren und ausführen, ist doch blöd, oder? Besser wäre doch, wenn wir unserem Programm direkt sagen könnten, wie hoch es zählen soll.

So, der aufmerksame Leser wird gemerkt haben, dass jetzt nach einem Modul gefragt ist, das MeinModul heisst, welche eine Prozedur namens ZaehlMal enthält, welches eine Zahl von der Eingabe liest, von 1 bis auf diese Zahl zählt, einen Wagenrücklauf einfügt und beendet. Musterlösung am Ende des Kapitels, bla bla bla...

Hoer mal -- das In-Modul

Bearbeiten

So, wie wir das Modul Out für die Ausgabe haben, haben wir so ein nettes Ding namens In (wer hätte es gedacht...), welches sich um die Eingabe kümmert. So, wie die Ausgabe zuerst mit Out.Open initialisiert werden muss, so muss dies auch mit der Eingabe geschehen:

In.Open;

Was macht eigentlich diese Anweisung? Out.Open öffnet bekanntlich ein Ausgabefenster, aber In.Open? Ein Eingabefenster? Nee! Die Eingabe kann man ja, wie eigentlich bekannt sein sollte, gleich nach dem Programmnamen einfügen und mit einer Tilde abschliessen, etwa so:

MeinModul.ZaehlMal 11~

Das Einzige, was dieses In.Open macht, ist, sowas wie einen Zeiger an den Anfang der Eingabe zu setzen, also in unserem Fall wäre das genau vor der 11. Wenn man jetzt also ein Zeichen, eine Zahl oder sonstwas einliest (wird alles später erklärt), rückt der Zeiger nach vorne. Will man wieder von vorne zu lesen anfangen, muss man das Zeiger-Ding wieder mit einem kräftigen In.Open an den Anfang zurücksetzen.

Polonius: "What do you read, my Lord?"; Hamlet: "Words, words, words."[1]

Bearbeiten

So, wir haben die Eingabe mit In.Open initialisiert, jetzt müssen wir nur noch lesen können. Dafür gibt es einige Befehle, welche ich jetzt erläutern werde:

  • In.Int(i): Liest eine ganze Zahl ein und speichert Sie in der Variablen i ab.
  • In.Char(c): Liest ein einziges Zeichen ein und speichert es in der Variablen c ab. Zu bemerken ist, dass zum Teil auch die Leerschläge vor der eigentlichen Eingabe so eingelesen werden.

Die weiteren Prozeduren des Moduls In werden uns erst später (wenn überhaupt) interessieren. Noch von speziellem Interesse ist vielleicht die Variable

In.Done : BOOLEAN;

Dieses eklige kleine Ding ist ziemlich paradox: ist die Eingabe noch nicht terminiert (d.h. man hat noch nicht bis zum ~ gelesen, ist sie TRUE. Versucht man jedoch über das Ende der Eingabe hinaus zu lesen, wird sie auf FALSE gesetzt, und dies dummerweise erst nachdem schon was schief gelaufen ist. In.Done spielt in der Fehlerüberprüfung eine Rolle indem man erwartete Elemente einlesen kann und dann In.Done testen: wenn es auf FALSE gesetzt ist, so ist was schief.

Alle User sind bloed

Bearbeiten

Es passiert allen mindestens einmal. Bei mir war es mit einem alten Apple ][e: ich hatte ein Progrämmchen geschrieben, welches von der Eingabe eine Zahl einlas und dann im Sekundentakt von dieser herunterzählte (naja, so ein "t minus x seconds"-Ding). Ganz stolz zeigte ich es meiner Schwester, welcher nichts besseres einfiel, als 10'000 als Eingabe einzugeben. Wie bricht man auf einem Apple ][e ein Programm ab, ohne es zu löschen? Das weiss ich immer noch nicht.

So, damit uns dies nicht passiert, müssen wir noch überprüfen, ob der User uns keinen Blödsinn angibt. Damit unsere ZaehlMal Prozedur keine Kopfschmerzen kriegt, weisen wir Eingabewerte von mehr als, sagen wir, 100 ganz frech zurück. Damit das Ganze auch noch einen Sinn hat, wollen wir auch keine Zahlen, welche kleiner als 1 sind.

Eigentlich ist das ganze nur ein fauler Trick um die IF-Anweisung einzuführen. Die sieht etwa so aus.

IF Bedingung THEN
    ...
END;

Das IF kündigt eine Bedingung an, welche auf TRUE oder FALSE aufgeloest wird. Ergibt sie TRUE, werden die Anweisungen zwischen THEN und END einmal durchgeführt. Toll, was?

Eine kleine Erweiterung fuer den Fall, dass man auch irgendwas besonderes machen muesste wenn Bedingung nicht erfuellt ist, sieht wie folgt aus

IF Bedingung THEN
    ...
ELSE
    ...
END;

Die Zeilen zwischen dem ELSE und dem END werden dann -- und nur dann ausgefuehrt, wenn Bedingung FALSE ist!

Fuer Fortgeschrittenen -- oder komplizierte -- stellen wir noch vor:

IF Bedingung1 THEN
    ...
ELSIF Bedingung2 THEN
    ...
ELSE
    ---
END;

Wie die meisten aufmerksamen Leser und Leserinnen wahrscheinlich schon erraten haben, werden, falls Bedingung1 TRUE ist, die Anweisungen zwischen dem THEN und dem ELSIF ausefuehrt. Is Bedingung1 FALSE und Bedingung2 dagegen TRUE werdien die Anweisungen zwischen dem zweiten THEN und dem ELSE ausgefuehrt. Man beachte, dass wenn beide Bedinungen TRUE sind, nur die ersten Anweisungen -- zwischen dem ersten THEN und dem ELSIF -- ausgefuehrt! Sind keines der Bedingungen TRUE so wird halt das ausgefuehrt, was zwischen dem ELSE und dem END steht.

Also, wir können jetzt prüfen, ob die Eingabe sinnvoll ist. Was ist aber, wenn sie es nicht ist? Wie kann man aus einem Programm rausspringen, wenn alles schiefgeht?

Eine Methode, die propagiert wird, ist die HALT-Anweisung. Ich ziehe aber ein geschickt plaziertes RETURN vor. Naja, wie soll man es sagen? Es ist wie bei einer Firma: mit RETURN werden die Pensionierfähigen sanft in den Ruhestand verabschiedet, wo das Leben dann ihr normales Ende findet, mit HALT erschiesst man sie einfach. Die Verwendung von HALT müsst ihr im Oberon-Buch nachlesen, so ein fieses Ding erkläre ich hier nicht, die RETURN-Anweisung jedoch schon:

RETURN;

Erfrischend einfach, oder? Das einzige, was RETURN macht, ist, an der Stelle an der es aufgerufen wird, aus der Prozedur rauszuspringen. Ist dies gerade die Hauptprozedur, so wird das Programm dadurch beendet. Natürlich, human.

Na gut, die Wappnung gegen blöde User ist jetzt gegeben:

In.Int(n);
IF ~In.Done OR (n > 100) OR (n < 1) THEN
    ...
    RETURN;
END;

"Come together..."[2]

Bearbeiten

Also, jetzt kommt alles zu einer Einheitlichkeit zusammen. Die Prozedur ZaehlMal muss jetzt die Eingabe initialisieren, eine Zahl einlesen, die Zahl auf Sinn oder Unsinn überprüfen, von 1 bis auf diese Zahl zählen, ein Wagenrücklauf ausspucken und sich friedlich verabschieden. Das sieht bei mir etwa so aus:

MODULE MeinModul;

IMPORT
    In, Out;

PROCEDURE ZaehlMal*;
    VAR
        n, zahl : INTEGER;
    BEGIN;
        In.Open;
        In.Int(n);
        IF ~In.Done OR (n > 100) OR (n < 1) THEN
            Out.String("Sag mal, du bloedes Ding, gib mir doch
                        eine Zahl zwischen 1 und 100!");
            Out.Ln;
            RETURN;
        END;
        zahl := 1;
        WHILE zahl <= n DO
            Out.Int(zahl,5);
            zahl := zahl + 1;
        END;
        Out.Ln;
    END ZaehlMal;

BEGIN
    Out.Open;
END MeinModul.

Fussnoten

Bearbeiten

Fussnote, s, f: Den Teil der Orgel-Partitur, den man mit den Füßen spielt.

  1. aus Hamlet, von William Shakespeare, ca. 1600.
  2. Titel eines Beatles-Liedes, vom Album Abbey Road, erschienen am 26. September 1969, bei Apple Records.