Kurs:Programmieren in Oberon/Kapitel 4
Dieses Kapitel gehoert zum Kurs Programmieren in Oberon des Fachbereichs Informatik.
Zaehlmal -- zweite Ausbauetappe
BearbeitenNa 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
BearbeitenNaja, 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
BearbeitenDie 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]
BearbeitenSo, 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
BearbeitenFussnote, s, f: Eine Geruchskomponente, ähnlich der ungewaschener unteren Extremitäten, "Dieser Sbrinz hat im Gegenteil zum Ementaler eine ausgeprägte Fussnote".
- ↑ Titel eines Beastie Boys-Liedes, vom Album Ill Communication, erschienen 1994 bei Capitol Records.