Pascal-Tutorial
Schleifen, Arrays
6.1. FOR-Schleife
Wagen wir uns nun zu den Wiederholungsanweisungen, bekannter unter der Bezeichnung Schleifen. Eine Schleife bietet dem Programmierer die Möglichkeit, Anweisungen kontrolliert oft ausführen zu lassen. Es findet also eine kontrollierte Wiederholung statt. Es stellt sich an dieser Stelle natürlich die Frage, wann man Schleifen in der Praxis benötigt. Vielleicht ist es für Sie in diesem Moment unvorstellbar, wie häufig Schleifen in der Praxis vorkommen. Anwendungsbeispiele sind beispielsweise die wiederholte Ausgabe/Eingabe von Daten. Weiters benötigt man Schleifen z.B. beim Lesen aus Dateien und Schreiben in Dateien. Einige weitere Fälle werden Sie im Laufe dieses Tutorials kennen lernen.
Pascal stellt drei verschiedene Schleifentypen zur Verfügung: die REPEAT-UNTIL-, die WHILE- und die FOR-Schleife. Am leichtesten zu verstehen ist hierbei die FOR-Schleife. Diese ist eine einzige Zählschleife.
Das bedeutet, dass der Programmierer einfach nur angeben muss, wie oft die Schleife durchlaufen werden soll. Angenommen, die Schleife sollte 100-mal ausgeführt werden. Nun ist es nötig, eine Zählervariable anzugeben. Diese ist wichtig, da abgespeichert werden muss, wie oft die Schleife bereits durchlaufen wurde. Jetzt fehlen nur mehr Start- und Höchst-Wert. Gibt der Programmierer als Startwert 1 und als Ziel-/Endwert 100 an, wird die Schleife 100-mal wiederholt. Bei jedem Durchgang wird der Wert in der Zählervariable um 1 erhöht (inkrementiert). Es ist ebenso möglich, die Schleife abwärts zählen zu lassen.
Soll die Schleife abwärts zählen, wird der Wert in der Zählervariable bei jedem Durchlauf um 1 erniedrigt (dekrementiert). Die Syntax der FOR-Schleife sieht wie folgt aus, wenn inkrementiert (hinaufgezählt) werden soll:
FOR Zählervariable := Von TO Bis DO Anweisung;
Sollen mehrere Anweisungen angegeben werden, müssen diese - wie üblich - in einem Anweisungsblock zusammengefasst werden:
FOR Zählervariable := Von TO Bis DO
BEGIN
Anweisung 1;
Anweisung 2;
Anweisung 3;
...
END;
Im nächsten Beispiel wird hinaufgezählt. Den Start- und Höchst-Wert kann der Benutzer hierbei selbst festlegen:
PROGRAM Hinaufzaehlen; USES Crt; VAR Startwert, Hoechstwert, i: Integer; BEGIN ClrScr; Write ('Geben Sie den Startwert ein: '); ReadLn (Startwert); Write ('Geben Sie den Höchstwert ein: '); ReadLn (Hoechstwert); FOR i := Startwert TO Hoechstwert DO WriteLn ('Aktueller Wert: ',i); WriteLn ('Drücken Sie eine Taste, um das Programm zu beenden ...'); ReadKey; END.
Die Zählervariable nennt man meistens i, was für Index steht. Zunächst werden die beiden Werte für Start- und Höchst-Wert eingelesen und in der FOR-Schleife angegeben. Bei jedem Durchlauf der FOR-Schleife wird der aktuelle Wert der Zählervariable i ausgegeben. Dies geschieht mittels WriteLn-Anweisung. Noch einmal zur Wiederholung: Der Ausgabetext steht unter einfachen Anführungszeichen und wird mit einem Beistrich (!) von der auszugebenden Variable getrennt.
Ersetzt man das TO durch ein DOWNTO, kann die Schleife abwärts zählen:
FOR Zählervariable := Von DOWNTO Bis DO Anweisung;
Im nächsten Beispiel wird hinuntergezählt. Ansonsten gleicht es dem letzten Beispiel.
PROGRAM Hinunterzaehlen; USES Crt; VAR Startwert, Mindestwert, i: Integer; BEGIN ClrScr; Write ('Geben Sie den Startwert ein: '); ReadLn (Startwert); Write ('Geben Sie den Mindestwert ein: (muss kleiner sein als der Startwert) '); ReadLn (Mindestwert); FOR i := Startwert DOWNTO Mindestwert DO WriteLn ('Aktueller Wert: ',i); WriteLn ('Drücken Sie eine Taste, um das Programm zu beenden ...'); ReadKey; END.
6.2. REPEAT-UNTIL-Schleife
Eine weitere Schleife ist die REPEAT-UNTIL-Schleife. Diese wiederholt einen Anweisungsblock solange, BIS eine bestimmte Bedingung erfüllt wird. Die Bedingung ist ein boolescher Ausdruck, ganz genau wie bei den Beispielen zur IF-Anweisung. Die Anweisungen, die zwischen REPEAT und UNTIL stehen, werden mindestens einmal ausgeführt. Das heißt, selbst wenn die Bedingung am Anfang erfüllt ist, werden die Anweisungen einmal ausgeführt. Man spricht hierbei von einer nicht-abweisenden Schleife, weil die Bedingung erst am Ende (nach einmaliger Ausführung des Anweisungsblocks) überprüft wird. Die REPEAT-UNTIL-Schleife hat folgende Syntax:
REPEAT
Anweisung 1;
Anweisung 2;
Anweisung 3;
...
UNTIL Bedingung;
Es fällt auf, dass die Anweisungen nicht zwischen BEGIN und END stehen dürfen/müssen, also ein "untypischer" Anweisungsblock. Das nächste Beispiel demonstriert die Verwendung dieser Schleife. Der Benutzer wird so lange aufgefordert, einen Text einzugeben, bis er - genau - Stopp eingibt.
PROGRAM Wiederholte_Eingabe; USES Crt; VAR Eingabe: String; BEGIN ClrScr; REPEAT Write ('Geben Sie "Stopp" ein, um das Programm zu beenden: '); ReadLn (Eingabe); UNTIL Eingabe = 'Stopp'; END.
Zur Wiederholung: Da wir nun Zeichenketten (eine Zeichenkette ist eine Folge von mehreren Zeichen; Zeichen sind z.B. a, x, 4 oder $) abspeichern, benötigen wir den Typ String. Dieser speichert Zeichenketten bis zu einer Länge von 255 Zeichen. Zunächst wird ein Text ausgegeben, der den Benutzer darauf hinweist, dass dieser einen Text eingeben soll. Danach wird in die Variable Eingabe ein Text eingelesen. Die Anweisungen nach REPEAT werden zunächst ausgeführt, egal, ob die Bedingung erfüllt ist oder nicht. Bei UNTIL angelangt, wird überprüft, ob in der Variable Eingabe der Wert Stopp steht. Ist die Bedingung erfüllt, wird die Schleife beendet. Ist sie allerdings nicht erfüllt, beginnt die Ausführung der zwischen REPEAT und UNTIL stehenden Anweisungen von neuem. Testen Sie das Programm einfach selbst und probieren Sie verschiedene Eingaben aus.
6.3. WHILE-Schleife
Fehlt nur noch die WHILE-Schleife. Diese wiederholt die im Anweisungsblock stehenden Anweisungen so lange, solange die Bedingung erfüllt ist. Ist die Bedingung (einmal) nicht erfüllt wird die Schleifenausführung beendet. Im Gegensatz zur REPEAT-UNTIL-Schleife steht bei der WHILE-Schleife die Bedingung am Anfang, was zur Folge hat, dass die Anweisungen im Anweisungsblock der Schleife überhaupt nicht ausgeführt werden, wenn die Bedingung anfangs nicht erfüllt ist. WHILE ist eine abweisende Schleife, da die Bedingung bereits zu Beginn überprüft wird. Die WHILE-Schleife hat folgende Syntax:
WHILE Bedingung DO Anweisung;
Die Bedingung muss wieder ein boolescher Ausdruck sein. Um mehrere Anweisungen auszuführen, müssen diese - wie üblich - in einem Anweisungsblock zusammengefasst werden. Das nächste Beispiel macht funktionell genau das Gleiche wie das letzte, jedoch wird anstelle einer REPEAT-UNTIL- eine WHILE-Schleife verwendet. Die Bedingung gehört dann quasi genau umgekehrt. Während die Schleife bei REPEAT-UNTIL beendet wird, wenn die Bedingung erfüllt ist, läuft die Schleife bei WHILE solange die Bedingung erfüllt ist.
PROGRAM Wiederholte_Eingabe_mit_WHILE; USES Crt; VAR Eingabe: String; BEGIN ClrScr; Eingabe := ''; (* initialisieren mit einer leeren Zeichenkette *) WHILE Eingabe <> 'Stopp' DO (* solange ungleich 'Stopp' ... *) BEGIN Write ('Geben Sie "Stopp" ein, um das Programm zu beenden: '); ReadLn (Eingabe); END; END.
6.4. Arrays
Angenommen, wir benötigen eine große Anzahl an Variablen vom gleichen Typ. Das kann vorkommen, wenn wir z.B. die Daten zu einer großen Menge von Leuten abspeichern wollen. Beispielsweise möchte man zu 20 Personen Namen + Telefonnummer abspeichern (werden wir im Moment nur im Arbeitsspeicher tun, in Kapitel 9 lernen Sie, wie Sie diese Daten in eine Datei schreiben). Da man die Namen und Telefonnummern in dem Zeitpunkt, in dem man das Programm schreibt noch nicht weiß (unsere Annahme im Moment), benötigen wir hierbei eine allgemeingültige Form wie kontakt1_name bis kontakt20_name für den Namen sowie kontakt1_tel bis kontakt20_tel für die Telefonnummern zu den richtigen Einträgen.
Allerdings haben wir nun grobe Probleme, hiermit zu arbeiten. Nicht nur, dass wir im VAR-Bereich des Programmes in diesem Beispiel 40 Variablen deklarieren müssten, das Einlesen in die Variablen sowie das Ausgeben der Daten ist ebenso umständlich. Um 20 Werte einzulesen, benötigte man 20 Zeilen mit Read(Ln)-Anweisungen. Um dieselben Daten wieder auszugeben, 20 Zeilen mit Write(Ln)-Anweisungen.
Am Rande: Variablennamen zur Laufzeit dynamisch zusammensetzen (z.B. 'kontakt' + Zählervariable + '_name' ergäbe den Variablennamen) geht unter Pascal nicht. So etwas geht etwa in PHP, wobei in PHP Variablen nicht explizit deklariert werden müssen und Datentypen erst zur Laufzeit feststehen (Dynamische Typisierung). Diese Idee kann man in Pascal also verwerfen. Wäre ohnehin eine sehr unschöne Lösung für diesen Zweck.
Wir lösen das Problem sauber und wie in Hochsprachen allgemein üblich, mit einer Schleife und der Verwendung eines Arrays. Ein Array ist quasi ein Feld bestehend aus mehreren Variablen gleichen Typs. Das Array (auch Datenfeld oder Matrix genannt) hat einen Namen. Um auf einen Bereich des Arrays zuzugreifen, benötigt man neben dem Namen des Arrays den Index des Elements. Über den Index (eine Zahl) geben Sie an, welches (= das wievielte) Element Sie ansprechen wollen.
Arrays werden ähnlich wie Variablen deklariert, hier ein eindimensionales Array:
VAR Name: Array [Von..Bis] of Typ;
Beispielsweise:
VAR Namen: Array [1..20] of String;
Hiermit hätten wir das Array Namen deklariert, das 20 Elemente enthält. Jedes dieser Elemente ist vom Typ String. Diese Elemente reichen von Namen[1] bis Namen[20]. Wir können auf ein Array wie auf eine normale Variable zugreifen, allerdings muss zusätzlich der Index (die Zahl, die zwischen den eckigen Klammern steht) angegeben werden. Um auf das 5. Element in diesem Beispiel zuzugreifen und diesem den Namen Claudia zuzuweisen, müssten wir Folgendes tun:
Namen[5] := 'Claudia';
Natürlich können Sie auch ein Array definieren, bei dem der untere Index 10 und der obere 15 ist. Nur hätte dann das erste Element den Index 10 und das letzte den Index 15. Das nächste Beispiel demonstriert, wie man 20 Werte (Namen + Telefonnummer) einlesen und später wieder ausgegeben kann:
PROGRAM Array_Beispiel; USES Crt; VAR Namen, Telefonnummern: Array [1..20] of String; i: Integer; BEGIN ClrScr; FOR i := 1 TO 20 DO BEGIN Write (i,' - Name: '); ReadLn (Namen[i]); Write (i,' - Telefonnummer: '); ReadLn (Telefonnummern[i]); END; WriteLn ('Die Daten werden nun wieder ausgegeben ...'); FOR i := 1 TO 20 DO WriteLn (Namen[i],' - ', Telefonnummern[i]); WriteLn ('Drücken Sie eine Taste, um das Programm zu beenden ...'); ReadKey; END.
Vielleicht fragen Sie sich, weshalb ich das Array Telefonnummern vom Typ String deklariert habe. Hiermit lassen sich Eingabefehler verhindern, sollte ein Benutzer z.B. vor die Landesvorwahl + schreiben oder / als Trennzeichen verwenden. Weiters können wir damit in gewissen Fällen Speicherplatz sparen und problemlos auch längere Telefonnummern abspeichern. Auch Leerzeichen lassen sich bedenkenlos eingeben.
In Pascal ist es auch möglich, mehrdimensionale Arrays zu verwenden. Das Array Daten im oberen Beispiel war ein eindimensionales Array. Das fiktive Array test[5] könnte man sich mit seinen 5 Elementen wie folgt vorstellen:
test[1] | test[2] | test[3] | test[4] | test[5] |
Wir haben eine Dimension, diese verläuft in der Darstellung entlang der x-Achse. Nehmen wir eine zweite hinzu, hier grafisch in Richtung der y-Achse, würde dies wie folgt aussehen:
test[1,1] | test[1,2] | test[1,3] | test[1,4] | test[1,5] |
test[2,1] | test[2,2] | test[2,3] | test[2,4] | test[2,5] |
test[3,1] | test[3,2] | test[3,3] | test[3,4] | test[3,5] |
test[4,1] | test[4,2] | test[4,3] | test[4,4] | test[4,5] |
test[5,1] | test[5,2] | test[5,3] | test[5,4] | test[5,5] |
In der oberen Tabelle wird das Array test[1..5,1..5] dargestellt. Das Element test[2,4] wurde rot in der Tabelle gekennzeichnet. Ein zweidimensionales Array wie dieses wird allgemein wie folgt deklariert:
VAR Name: Array [Von..Bis, Von..Bis] of Typ;
Es lassen sich natürlich auch n-dimensionale Arrays definieren. Beispielsweise ein 4-dimensionales Array:
VAR Name: Array [Von..Bis, Von..Bis, Von..Bis, Von..Bis] of Typ;
Ein Beispiel erspare ich mir an dieser Stelle. Arrays dieser Dimension lassen sich weder vernünftig grafisch darstellen, noch werden Sie sie häufiger - wenn überhaupt - brauchen.
Vorheriges Kapitel | Nächstes Kapitel |