Kurs:Programmieren in Oberon/Kapitel 4

Aus Wikiversity

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

Zaehlmal -- zweite Ausbauetappe[Bearbeiten]

Na schön, wir können jetzt unserem Programm sagen, was es tun soll (naja, ziemlich beschränkt, aber geht noch so). Also nun, das Ding wird auch langsam kompliziert und lange, und es wäre doch schön, Teile des Programms, welche eigentlich wenig miteinander zu tun haben, übersichtshalber voneinander abtrennen zu können

Für diejenigen, die schon voraus sind, gilt es jetzt, die Eingabe in einer Prozedur abzutrennen und zwar nicht nur ein Argument zu lesen, sondern drei. Unser Programm soll jetzt die Werte von, bis und schritt einlesen und von von bis bis im Schritt schritt zählen und zu jeder Zahl noch das Quadrat ausgeben. Viel Glück.

Noch mehr wunderbares zur Welt der PROCEDURE-Anweisung[Bearbeiten]

Naja, die meisten von euch werden schon gewusst haben, dass man das Programm in kleinere Prozeduren aufteilen und diese dann von einer Hauptprozedur aus alle nacheinander ausführen kann, und die, die es bis jetzt noch nicht gewusst haben, die wissen's jetzt auch.

Wie ruft man von einer Prozedur aus eine andere auf? Nanu, ohne es zu wissen haben wir es schon mal getan (oje!), zum Beispiel:

In.Open;

oder vielleicht noch exotischer

Out.Int(zahl,5);

War doch einfach, nicht? Das können wir doch auch:

MODULE MeinModul;

    IMPORT
        Out;

    PROCEDURE MeldeWas;
        BEGIN
            Out.String("Hallo, Niklaus!");
            Out.Ln;
        END MeldeWas;

    PROCEDURE SagHallo*;
        BEGIN
            MeldeWas;
        END SagHallo;

    BEGIN
        Out.Open;
    END MeinModul.

Compiliert ihr das ganze und führt dann MeinModul.SagHallo aus, so wird natürlich die Prozedur SagHallo ausgeführt Diese springt jedoch mit dem Aufruf

MeldeWas;

in die Prozedur MeldeWas hinein, führt dort alle Anweisungen bis zu einem RETURN oder dem END der Prozedur aus und springt wieder in die Prozedur SagHallo zurück, wo sie aufgerufen wurde. Einfach, oder?

Funktionen als Prozeduren verkleidet[Bearbeiten]

Die meisten unter euch werden sicher mit dem Begriff Funktion aus der Mathematik vertraut sein (wenn nicht, dann oje!). Funktionen funktionieren meistens so, dass man einer Funktion einen Wert (oder auch mehrere) liefert (die heissen übrigens üblicherweise Parameter), und diese dann irgendwas damit macht und ein Resultat zurückgibt. Prozeduren können das natürlich auch, wobei weder eine Wertübergabe noch ein Resultat zwingend sind.

Ein Beispiel einer Prozedur mit Parametern wäre, auf unsere solide Programmiererfahrung zurückgreifend,

MODULE MeinModul;

    IMPORT
        Out;

    PROCEDURE Zaehl ( n : INTEGER );
        VAR
            zahl : INTEGER;
        BEGIN
            zahl := 1;
            WHILE zahl <= n DO
                Out.Int(zahl,5):
                zahl := zahl + 1;
            END;
        END Zaehl;

    PROCEDURE zaehlAufZehn*;
        BEGIN
            Zaehl(10);
            Out.Ln;
        END zaehlAufZehn;

    BEGIN
        Out.Open;
    END MeinModul.

So, anstatt Zaehl als eine normale PROCEDURE zu deklarieren, haben wir noch ( n : INTEGER ) hinzugefügt. Dies ist gleichwertig mit der Deklaration von n als Variable vom Typ INTEGER in Zaehl, jedoch mit dem Unterschied, dass n auf den Wert gesetzt wird, welcher Zaehl beim Aufruf mitgegeben wurde, in unserem Beispiel also auf 10 (dieser Wert muss natürlich auch vom Typ INTEGER sein).

Wie gesagt, verhält sich n in Zaehl wie eine lokal deklarierte Variable. Man kann also ihren Wert ändern und sonstwie missbrauchen, ohne dass sich global etwas verändert.

Wie sieht es aber aus, wenn eine Prozedur (oder jetzt eher Funktion) etwas zurückgeben muss? Wie schon erwähnt, kann man mit der Anweisung RETURN aus einer Prozedur rausspringen. Dieselbe Anweisung wird benutzt, um ein Wert zurückzugeben. Nehmen wir als Beispiel eine Funktion, welche eine Zahl als Parameter bekommt und dessen Quadrat zurückgibt

MODULE MeinModul;

    IMPORT
        In, Out;

    PROCEDURE Quadrat ( x : INTEGER ) : INTEGER;
        BEGIN
            RETURN x*x;
        END Quadrat;

    PROCEDURE MachWas*;
        VAR
            x : INTEGER;
        BEGIN
            In.Open; In.Int(x);
            Out.Int(x,3); Out.String(" im Quadrat ist ");
            Out.Int(Quadrat(x),3);
            Out.String("."); Out.Ln;
        END MachWas;

    BEGIN
        Out.Open;
    END MeinModul.

Das Anhängsel ": INTEGER" in der PROCEDURE-Anweisung dient dazu, dem Compiler zu sagen, dass die Prozedur einen Wert vom Typ INTEGER zurückgibt. Der Typ der Variable in der RETURN-Anweisung muss dementsprechend auch INTEGER sein.

Zu bemerken sei nur noch, dass sowohl die Parameter als auch die Rückgabe nur Werte sind, d.h. sie werden, jetzt in unserem Beispiel, wie Zahlen behandelt und nicht wie Variablen.

Variablen ausleihen[Bearbeiten]

"Jaaberhallo" werden die meisten von euch gesagt haben, als sie den letzten Satz gelesen haben. Naja, ok, man kann auch Variablen anstatt Werte übergeben, es geht jedoch ein wenig anders. Nehmen wir als Beispiel die Prozedur

In.Int(n);

Man gibt ihr eine Variable n und sie ändert deren Wert. Schaut man in der Datei In.Mod nach, sieht man bei der Deklaration von In.Int folgendes:

PROCEDURE Int* ( VAR i : INTEGER );

Unsere Aufmerksamkeit gilt dem Wörtchen VAR. Dieses sagt dem Compiler, dass die PROCEDURE nicht eine eigene Variable mit dem Wert von i kreieren, sondern dass sie gerade die Variable, die ihr gegeben wird, benutzen soll. Was heisst das konkret? Ändert man jetzt in Int den Wert von i, so wird der Wert der zugewiesenen Variablen in der aufrufenden Prozedur verändert. Kompliziert? Beispiel!

MODULE MeinModul;

    IMPORT
        In, Out;

    PROCEDURE Quadriere ( VAR x : INTEGER );
        BEGIN
            x := x * x;
        END Quadriere;

    PROCEDURE MachNix ( x : INTEGER );
        BEGIN
            x := x * x;
        END MachNix;

    PROCEDURE Test*;
        VAR
            i : INTEGER;
        BEGIN
            In.Open; In.Int(i); Out.Int(i,5);
            Quadriere(i); Out.Int(i,5);
            MachNix(i); Out.Int(i,5);
        END Test;

    BEGIN
        Out.Open;
    END MeinModul.

Compiliert man das Zeugs und führt es dann mit z.B. MeinModul.Test 5~ aus, so erhält man folgendes als Resultat:

5   25   25

Die Prozedur Quadriere hat den Parameter x als VAR deklariert, also verändert sie den Wert von i, welcher ihr in Test mitgegeben wurde. Die Prozedur MachNix hat dies jedoch nicht getan und verändert den Wert der Variablen in Test nicht.

Get it together[1][Bearbeiten]

So, jetzt kommt unser gesamtes Wissen zusammen, und wir schreiben damit eine Quadrate-Tabelle mit Eingabekontrolle, welche auch noch schön in Prozeduren aufgeteilt ist. Bei mir geht das etwa so:

MODULE MeinModul;

    IMPORT
        In, Out;

    PROCEDURE Ein ( VAR von, bis, schritt : INTEGER ) : BOOLEAN;
        BEGIN
            In.Open;
            In.Int(von); In.Int(bis); In.Int(schritt);
            IF ~In.Done OR (von > bis) OR 
               (bis-von DIV schritt > 100) THEN
                Out.String("He, Du trottel, gib mir doch was
                            anstaendiges!"); Out.Ln;
                RETURN FALSE;
            ELSE
                RETURN TRUE;
        END Ein;

    PROCEDURE Quadrat ( x : INTEGER ) : INTEGER;
        BEGIN
            RETURN x*x;
        END Quadrat;

    PROCEDURE Aus ( von, bis, schritt : INTEGER );
        VAR
            zahl : INTEGER;
        BEGIN
            zahl := von;
            WHILE zahl <= bis DO
                Out.Int(zahl,3); Out.String(" -> ");
                Out.Int(Quadrat(zahl),5); Out.Ln;
                zahl := zahl + schritt;
            END;
        END Aus;

    PROCEDURE ZaehlMal*;
        VAR
            von, bis, schritt : INTEGER;
        BEGIN
            IF Ein(von,bis,schritt) THEN
                Aus(von,bis,schritt);
            END;
        END ZaehlMal;

    BEGIN
        Out.Open;
    END MeinModul.

Fussnote[Bearbeiten]

Fussnote, s, f: Eine Geruchskomponente, ähnlich der ungewaschener unteren Extremitäten, "Dieser Sbrinz hat im Gegenteil zum Ementaler eine ausgeprägte Fussnote".

  1. Titel eines Beastie Boys-Liedes, vom Album Ill Communication, erschienen 1994 bei Capitol Records.