GNU-Pascal in Beispielen: Kontrollstrukturen
zurück zu GNU-Pascal in Beispielen
Kontrollstrukturen
[Bearbeiten]Bislang haben wir die Programmierung als eine lineare Abfolge von Befehlen kennen gelernt. Das Programm fing oben an und hörte unten auf, dazwischen war, mehr oder weniger unabhängig von der Benutzereingabe, der komplette Programmfluss statisch vorgegeben. Kontrollstrukturen sorgen dafür, dass je nach Gegebenheit ein anderer Code ausgeführt wird oder ein bestimmter Abschnitt des Programms nach bestimmten Regeln wiederholt wird.
if-then
[Bearbeiten]Die if-Abfrage dient dazu, auf Eingaben, Zwischenergebnisse oder "Benutzerfehler" zu reagieren. Das folgende Beispiel ist ein modifiziertes "StringNachZahl"-Programm [1] mit Fehlerabfrage:
Programm: StringNachZahl2
[Bearbeiten]program StringNachZahl2;
var
Zahlentext: String (20);
Zahl: Integer;
Fehler: Integer;
begin
Write ('Geben Sie eine ganze Zahl ein: ');
ReadLn (Zahlentext);
Val (Zahlentext, Zahl, Fehler);
if Fehler = 0 then
WriteLn ('Hervorragend!, Ihre Zahl lautet: ', Zahl);
if Fehler <> 0 then
begin
WriteLn ('Fehler! Sie sollten doch eine ganze Zahl');
WriteLn ('eingeben und nicht ''', Zahlentext, '''!');
end
end.
Erklärung
[Bearbeiten]Das Programm erwartet als Eingabe eine ganze Zahl, die es als String entgegennimmt und mit Hilfe von Val in eine ganze Zahl umwandelt. Wenn der Wert von Fehler bei dieser Umwandlung 0 ist, so wird die dazugehörige Anweisung WriteLn ('Hervorragend...'); ausgeführt. Den Bereich zwischen if und then kennen Sie bereits, dabei handelt es sich um einen Boolschen Ausdruck, eine so genannte Bedingung, die zu True oder False ausgewertet wird. Ergibt die Bedingung True, so wird die dazugehörige Anweisung ausgeführt.
Die zweite if-Abfrage überprüft, ob vielleicht doch ein Fehler aufgetreten ist. Wenn die Variable Fehler einen Wert hat, der verschieden von "0" ist, so wird der Anweisungsblock zwischen begin...end ausgeführt. Da hier zwei WriteLn-Anweisungen ausgeführt werden sollen, muss der Bereich auf diese Weise mit begin und end zusammengefasst werden. Bei einer einzelnen Anweisung ist es selbstverständlich, dass sie zur vorhergehenden if-Anweisung gehört. Bitte beachten Sie bei diesem Beispiel auch die Stellen, an denen Semikolons gesetzt werden: Immer nur zwischen zwei Anweisungen, wobei end keine Anweisung darstellt.
Ein Beispiel für zusammengesetzte Bedingungen ist eine Aufschrift auf einem Kinderkarussell: Nur Personen, die maximal 14 Jahre alt und kleiner als 1.2m sind dürfen dort mitfahren. Das Gegenteil dieser Bedingung lautet [2]: Personen, die älter als 14 Jahre sind oder mindestens 1.2m groß sind. Folgendes Beispiel verdeutlicht das:
Programm: Kinderkarussell1
[Bearbeiten]program Kinderkarussell1;
var
Alter, Groesse: Integer;
begin
Write ('Wie alt bist Du?: ');
ReadLn (Alter);
Write ('Wie groß bist Du (in cm)?: ');
ReadLn (Groesse);
if (Alter <= 14) and (Groesse < 120) then
WriteLn ('Du darfst auf das Kinderkarussell!');
if (Alter > 14) or (Groesse >= 120) then
WriteLn ('Du darfst nicht auf das Kinderkarussell.');
end.
Erklärung
[Bearbeiten]Bei zusammengesetzten Bedingungen werden die einzelnen Bedingungen geklammert. Das liegt daran, dass, wie bei der "Punkt- vor Strichrechnung", die Operatoren and bzw. or eine höhere Priorität haben als <, <=, >, >=. Statt not (Alter <= 14)... in der zweiten if-Abfrage zu verwenden, wurde dieser Ausdruck fertig berechnet.
else
[Bearbeiten]Der else-Ausdruck verzweigt in die Alternative zu if. Das obige Beispiel des Kinderkarussells sieht umgeschrieben unter Verwendung Verwendung von else folgendermaßen aus:
Programm: Kinderkarussell2
[Bearbeiten]program Kinderkarussell2;
var
Alter, Groesse: Integer;
begin
Write ('Wie alt bist Du?: ');
ReadLn (Alter);
Write ('Wie groß bist Du (in cm)?: ');
ReadLn (Groesse);
if (Alter <= 14) and (Groesse < 120) then
WriteLn ('Du darfst auf das Kinderkarussell!')
else
WriteLn ('Du darfst nicht auf das Kinderkarussell.')
end.
Erklärung
[Bearbeiten]Else spart uns die zweite if-Abfrage aus dem oberen Beispiel. Jeder Grund, warum eine Person nicht auf das Kinderkarussell darf wird von diesem Ausdruck abgefangen. Das Programm wird dadurch lesbarer und es wird nur eine (zusammengesetzte) Bedingung ausgewertet, nicht zwei.
Mehrfachverzweigung mit if
[Bearbeiten]Die hier vorgestellten Verzweigungen dürfen beliebig ineinander geschachtelt werden:
Programm: Kinderkarussell3
[Bearbeiten]program Kinderkarussell3;
var
Alter, Groesse: Integer;
begin
Write ('Wie alt bist Du?: ');
ReadLn (Alter);
if Alter <= 14 then
begin
Write ('Wie groß bist Du (in cm)?: ');
ReadLn (Groesse);
if Groesse < 120 then
WriteLn ('Du darfst auf das Kinderkarussell!')
else
WriteLn ('Du darfst nicht auf das Kinderkarussell.')
end
else
WriteLn ('Du bist zu alt für das Kinderkarussell.')
end.
Erklärung
[Bearbeiten]Dieses Beispiel stuft die zu einem Scheitern des Versuches führenden Gründe, auf das Kinderkarussell zu kommen, viel feiner ab. Außerdem wird bei einem zu hohen Alter keine weitere Abfrage benötigt. Falls das Alter passt, wird der gesamte innere begin...end-Block durchlaufen.
Eine andere Art der Mehrfachauswahl stellt das folgende Programm dar:
Programm: Kinderkarussell4
[Bearbeiten]program Kinderkarussell4;
var
Alter: Integer;
begin
Write ('Wie alt bist Du?: ');
ReadLn (Alter);
if Alter <= 14 then
WriteLn ('Du darfst auf das Kinderkarussell.')
else if Alter >= 18 then
begin
WriteLn ('Wir suchen noch Mitarbeiter für das ');
WriteLn ('Fahrgeschäft. Bitte melde Dich in der',
' Personalabteilung.')
end
else
WriteLn ('Du darfst leider nicht auf das Kinderkarussell')
end.
Erklärung
[Bearbeiten]Hier wird aus drei Bereichen ausgewählt: Die Person kann 14 Jahre oder jünger sein, 18 Jahre oder älter oder ein anderes Alter haben. Je nach Alter wird entsprechend verzweigt.
Mehrfachverzweigung mit case
[Bearbeiten]Das letzte Kinderkarussell kann noch viel feiner differenzieren und benötigt dafür viel weniger Zeilen, ist damit übersichtlicher ohne den Komfort zu verlieren:
Programm: Kinderkarussell5
[Bearbeiten]program Kinderkarussell5;
var
Alter: Integer;
begin
Write ('Wie alt bist Du?: ');
ReadLn (Alter);
case Alter of
0: WriteLn ('Du bist bestimmt zu jung!');
3..14: WriteLn ('Du darfst auf das Kinderkarussell');
18..26: begin
WriteLn ('Wir suchen noch Mitarbeiter für',
' das Fahrgeschäft. Bitte melde Dich in');
WriteLn ('der Personalabteilung.')
end
otherwise
WriteLn ('Du darfst leider nicht auf das Kinderkarussell')
end
end.
Erklärung
[Bearbeiten]Mit case Alter of beginnt der so genannte case-Block, der erst am gleich eingerückten end endet. Dazwischen befinden sich "case-Labels", die entweder sehr scharf (0:) oder innerhalb eines aufzählbaren und konstanten [3] Bereiches (3..14:) das Alter abfragen. Das Schlüsselwort otherwise dient dazu, alle Fälle abzufangen, für die kein "case-Label" vorgesehen ist. Dies entspricht dem else [4] in if-Abfragen.
Schleifen
[Bearbeiten]Schleifen dienen dazu, sich wiederholenden Programmcode übersichtlich zu formulieren. Außerdem wird es mit der Verwendung von Schleifen möglich, einen Programmabschnitt beliebig oft zu wiederholen. Letztlich werden nur die Bedingungen notiert, unter denen sich der Code wiederholen muss und der Quellcode wird genau einmal geschrieben. Dieser sich wiederholende Programmteil wird Schleifenkörper oder Schleifenrumpf genannt. Es gibt drei verschiedenen Arten von Schleifen.
Schleifen mit for
[Bearbeiten]Eine for-Schleife dient dazu, Programmcode eine bekannte Anzahl Mal ausführen zu lassen:
Programm: Schleife1
[Bearbeiten]program Schleife1;
const
Text = 'Hallo, Welt!';
var
i: Integer;
begin
for i := 1 to Length (Text) do
WriteLn (i, '-ter Buchstabe ist ', Text[i], '.')
end.
Erklärung
[Bearbeiten]Mit dieser Schleife wird jeder Buchstabe des Textes ausgegeben. Die Variable i ist dabei zu Beginn 1 und zum Schluss Length (Text), dazwischen hat sie jeden Wert in aufsteigender Reihenfolge genau einmal. Der Schleifenkörper gibt dabei den Wert der Variablen aus und den Buchstaben an der gegenwärtigen Position. Das Schüsselwort to in der oberen for-Anweisung sorgt dafür, dass vorwärts gezählt wird. Würden wir rückwärts zählen wollen, also den Text in umgekehrter Reihenfolge ausgeben wollen, so müssten wir das Schlüsselwort downto verwenden, also for i := Length (Text) downto 1 do.
Wie im Kapitel FF: set-section über Mengen angemerkt, folgt hier das Beispiel zum Thema Durchlaufen von Mengen:
Programm: Menge2
[Bearbeiten]program Menge2;
var
MeineMenge: set of Char;
Element: Char;
begin
MeineMenge := ['a'..'c', 'B', 'C'];
for Element in MeineMenge do
begin
Write ('Meine Menge enthält das Element: ', Element);
WriteLn (' mit der Ordnung: ', Ord (Element))
end
end.
Erklärung
[Bearbeiten]Da die Mächtigkeit [5] der Menge zu Beginn der Schleife feststeht, kann die Menge auf diese Weise durchlaufen werden. Element enthält zu Beginn des Schleifendurchlaufs allerdings den Wert 'B', nicht etwa 'a', wie es die Mengeninitialisierung nahelegt, weil es der Wert mit der kleinsten Ordnung ist, wie sich leicht bei einem Programmlauf feststellen lässt:
Meine Menge enthält das Element: B mit der Ordnung: 66 Meine Menge enthält das Element: C mit der Ordnung: 67 Meine Menge enthält das Element: a mit der Ordnung: 97 Meine Menge enthält das Element: b mit der Ordnung: 98 Meine Menge enthält das Element: c mit der Ordnung: 99
Der Inhalt von Variablen vom Typ einer Aufzählung (vergl. FF aufz-section) lässt sich nicht direkt auf den Bildschirm schreiben. Wie sollte auch der Computer mit einem Befehl umgehen, der "Schreibe Rot auf den Bildschirm" heißt? Meint der Anwender einen roten Apfel oder das Wort "Rot"? Hierbei hilft die case-Anweisung:
Programm: Aufzaehlung2
[Bearbeiten]program Aufzaehlung2;
var
Farbe: (Rot, Gelb, Blau);
begin
for Farbe := Rot to Blau do
case Farbe of
Rot: WriteLn ('Rot');
Gelb: WriteLn ('Gelb');
Blau: WriteLn ('Blau')
end
end.
Erklärung
[Bearbeiten]Dieses Programm schreibt nacheinander die Worte "Rot", "Gelb" und "Blau" auf den Bildschirm. Schleifen lassen sich vorher beenden, hierzu dient der Ausdruck Break. Folgendes Programm zeigt dessen Arbeitsweise:
Programm: Schleife2
[Bearbeiten]program Schleife2;
var
Anzahl: Integer;
begin
for Anzahl := 1 to 10 do
begin
WriteLn (Anzahl, ' ist da.');
if Anzahl = 3 then
Break
end;
end.
Erklärung
[Bearbeiten]Es werden nur die ersten Drei WriteLn-Anweisungen ausgeführt. Nach der dritten Anweisung wird Break aufgerufen wird. Die Schleife wird damit unverzüglich verlassen.
Ebenso wie es eine Anweisung für das Verlassen von Schleifen gibt, gibt es eine Anweisung, um wieder in den Programmkopf zurückzukehren ohne den Rest des Schleifenkörpers auszuführen:
Programm: Schleife3
[Bearbeiten]program Schleife3;
var
Anzahl: Integer;
begin
for Anzahl := 1 to 10 do
begin
if Anzahl Mod 2 = 0 then
Continue;
WriteLn (Anzahl, ' ist da.')
end;
end.
Erklärung
[Bearbeiten]Wenn die Integer-Division von Anzahl und 2 den Rest 0 ergibt, was bei allen geraden Zahlen der Fall ist, dann sorgt Continue dafür, dass sofort in den Schleifenkopf zurückgesprungen wird ohne die benachbarte WriteLn-Anweisung auszuführen. Dadurch werden nur ungerade Zahlen ausgegeben.
Schleifen mit while
[Bearbeiten]Mit dem Schlüsselwort While wird die abweisende Schleife gebildet. Das Besondere an abweisenden Schleifen ist, dass schon in der Schleifenbedingung überprüft wird, ob diese Schleife überhaupt durchlaufen wird. Damit wird diese Schleife gegebenenfalls nie durchlaufen. Den Schleifenkörper auszuführen kann while also abweisen.
Programm: Wuerfelglueck1
[Bearbeiten]program Wuerfelglueck1;
const
ZufallGrenze = 10;
WunschZahl = 5;
var
Anzahl: Integer = 1;
Zufall: Integer;
begin
Zufall := Random (ZufallGrenze);
WriteLn ('ZufallsZahl = ', Zufall);
while Zufall <> WunschZahl do
begin
Zufall := Random (ZufallGrenze);
WriteLn ('ZufallsZahl = ', Zufall);
Inc (Anzahl)
end;
WriteLn ('Gefunden nach ', Anzahl, ' Versuchen')
end.
Erklärung
[Bearbeiten]Zuerst wird eine Zufallszahl im Bereich von 0 bis ZufallGrenze - 1 gezogen. Wenn die Zufallszahl nicht der Wunschzahl entspricht, wird der Schleifenkörper durchlaufen und die Anzahl der Versuche, eine passende Zufallszahl zu ziehen wird protokolliert. Dies geschieht mit Hilfe der Prozedur Inc. Diese Prozedur ist gleichbedeutend mit der Anweisung Anzahl := Anzahl + 1, was bedeutet, die Variable Anzahl um eins zu erhöhen. Am Ende des Programms wird die Anzahl der Versuche ausgegeben. Das "Gegenteil" von Inc ist Dec, was die Variable um eins erniedrigt.
Schleifen mit repeat...until
[Bearbeiten]Repeat...until ist eine nichtabweisende Schleife. Nichtabweisende Schleifen werden mindestens einmal durchlaufen, weil die Abbruchbedingung am Ende des Schleifenkörpers liegt. Der Schleifenkörper braucht im Falle von mehreren Anweisungen nicht von begin und end umschlossen zu werden, da repeat und until selbst einen Block bilden. Ein positiver Nebeneffekt bei der Benutzung dieser Schleife ist die geringere Einrückung. Obiges Beispiel sieht umgeschrieben so aus:
Programm: Wuerfelglueck2
[Bearbeiten]program Wuerfelglueck2;
const
ZufallGrenze = 10;
WunschZahl = 5;
var
Anzahl: Integer = 0;
Zufall: Integer;
begin
repeat
Zufall := Random (ZufallGrenze);
WriteLn ('ZufallsZahl = ', Zufall);
Inc (Anzahl)
until Zufall = WunschZahl;
WriteLn ('Gefunden nach ', Anzahl, ' Versuchen')
end.
Erklärung
[Bearbeiten]Im Gegensatz zu Wuerfelglueck1 wird die Variable Anzahl hier mit 0 initialisiert. Der Schleifenkörper, ab repeat wird mindestens einmal durchlaufen, dabei wird der Zufallswert ermittelt und die Anzahl der Versuche auf "1" gesetzt. Der Schleifenkopf soll durchlaufen werden, bis Zufall = WunschZahl ist. Dann wird, wie im entsprechenden while-Beispiel, die Anzahl der Versuche ausgegeben.
Programmierbeispiel: Zahlenraten
[Bearbeiten]Das folgende Beispiel implementiert mit den uns bekannten Mitteln das Spiel "Zahlenraten", bei dem es darum geht, eine Zufallszahl zwischen 1 und 100 zu erraten. Die einzigen Hilfestellungen sind die Hinweise "Zu groß" und "Zu klein":
Programm: Zahlenraten
[Bearbeiten]program Zahlenraten;
const
ObereGrenze = 100;
var
Anzahl: Integer = 0;
Zufallszahl, RateZahl: Integer;
begin
Randomize;
Zufallszahl := Random (ObereGrenze) + 1;
Write ('Ich denke mir eine Zufallszahl zwischen 1 und ');
WriteLn (ObereGrenze, ' aus, die Sie raten müssen.');
repeat
Write ('RateZahl: ');
ReadLn (RateZahl);
Inc (Anzahl);
if RateZahl > ZufallsZahl then
WriteLn ('Zu groß!')
else if RateZahl < ZufallsZahl then
WriteLn ('Zu klein!')
until Zufallszahl = RateZahl;
WriteLn ('Gefunden nach ', Anzahl, ' Versuchen')
end.
Erklärung
[Bearbeiten]Mit Random wird eine Zufallszahl erzeugt, die es zu raten gilt. Vom Benutzer wird ein Rateversuch eingegeben, die Anzahl der Versuche, die richtige Zahl zu finden wird protokolliert (Inc (Anzahl)). Je nachdem, ob die geratene Zahl zu groß oder zu klein ist, wird ein Hinweis ausgegeben. Der Schleifenkörper wird so lange durchlaufen, wie die Zufallszahl und die Ratezahl nicht übereinstimmen.
Programmierbeispiel: Palindrom
[Bearbeiten]Ein Palindrom ist ein Wort [6], welches von hinten und von vorne gleich gelesen werden kann. Beispiele für solche Worte sind "OTTO" und "UHU". Dieses Programm testet ein Wort darauf, ob es ein Palindrom ist:
Programm: Palindrom
[Bearbeiten]program Palindrom;
var
Wort: String (100);
i, Laenge: Integer;
IstPalindrom: Boolean = True;
begin
WriteLn ('Das Programm untersucht, ob ein eingegebenes');
WriteLn ('Wort ein Palindrom ist.');
Write ('Geben Sie ein Wort ein: ');
ReadLn (Wort);
Laenge := Length (Wort);
for i := 1 to Laenge div 2 do
begin
if Wort[i] <> Wort[Laenge - i + 1] then
begin
IstPalindrom := False;
Break
end
end;
if IstPalindrom then
WriteLn ('''', Wort, ''' ist ein Palindrom.')
else
WriteLn ('''', Wort, ''' ist kein Palindrom.')
end.
Erklärung
[Bearbeiten]Vom eingegebene Wort wird die Länge benötigt, da der Algorithmus überprüft, ob links und rechts der Mitte des Wortes die Buchstaben übereinstimmen.
Der Algorithmus sieht wie folgt aus: Wenn der erste Buchstabe und der Letzte nicht übereinstimmen, so ist das Wort kein Palindrom. Wenn der Zweite und der vorletzte Buchstabe nicht übereinstimmen, so ist das Wort ebenfalls kein Palindrom..., sonst ist es ein Palindrom. Die Bedingung Wort[i] <> Wort[Laenge - i + 1] überprüft genau diese Bedingung. i ist dabei der Zähler, der bis maximal zur Mitte des Wortes reicht [7].
Wort[i] und Wort[Laenge - i + 1] sind spiegelbildliche Buchstabenpositionen innerhalb des Wortes. Die +1 kommt daher, dass der Anfangsindex des Wortes Eins ist. Wenn zu Beginn auf das letzte Zeichen des Wortes zugegriffen werden soll, so ist damit Wort[Laenge] gemeint, nicht etwa Wort[Laenge - i] (wobei i = 1 ist). Die Addition von 1 führt genau zu dieser Korrektur.
Anmerkungen
[Bearbeiten]- ↑ vgl. Kapitel Variablen und Typen
- ↑ Nach den Morganschen Regeln: not (A and B) = not A or not B.
- ↑ Als "case-Labels" dürfen keine Variablen verwendet werden.
- ↑ tatsächlich ist hier auch else erlaubt. Die Wahl des Wortes ist Geschmackssache.
- ↑ Synonym für Anzahl der Elemente innerhalb der Menge.
- ↑ Ein Palindrom kann auch ein Satz sein. In diesem Beispiel würden die Stellungen der Leerzeichen mitberücksichtigt werden, so dass bekannte palindrome Sätze nicht funktionieren.
- ↑ Bei ungeraden Wortlängen bis ein Buchstaben vor der Mitte, da einbuchstabige Worte palindrom sind. Bei geraden Wortlängen gibt es keinen mittleren Buchstaben oder derer zwei, wie man will