Logo

Marco Cantù's
Essential Pascal

Kapitel 5
Anweisungen

Wenn Datentypen eine Grundlage der Pascal-Programmierung bilden, so sind Anweisungen die andere. Anweisungen der Programmiersprache basieren auf Schlüsselwörtern und weiteren Elementen, die es Ihnen gestatten einem Programm eine Reihenfolge von Operationen anzuzeigen, die dieses ausführen soll. Anweisungen sind häufig in Prozeduren und Funktionen eingeschlossen, wie wir im nächsten Kapitel noch sehen werden. Jetzt werden wir uns zunächst auf die Grundtypen von Anweisungen konzentrieren, die Sie verwenden können, um ein Programm zu erstellen.

Einfache und zusammengesetzte Anweisungen

Eine einfache Pascal-Anweisung liegt dann vor, wenn sie keine anderen Anweisungen enthält. Beispiele für einfache Anweisungen sind Zuweisungen und Prozeduraufrufe. Einfache Anweisungen werden durch ein Semikolon voneinander getrennt:

X := Y + Z;  // Zuweisung
Randomize;   // Prozeduraufruf

Gewöhnlich sind Anweisungen Teil einer zusammengesetzten Anweisung, dir durch begin- und end-Klammern markiert sind. Eine zusammengesetzte Anweisung kann an Stelle einer allgemeinen Pascal-Anweisung vorkommen. Hier ist ein Beispiel:

begin
  A := B;
  C := A * 2;
end;

Das Semikolon nach der letzten Anweisung vor dem end ist nicht erforderlich, wie im Ffolgenden:

begin
  A := B;
  C := A * 2
end;

Beide Versionen sind korrekt. Die erste Version besitzt eine unnötiges (aber unschädliches) Semikolon. Dieses Semikolon bildet eine Leeranweisung; d.h. eine Anweisung ohne Code. Beachten Sie, dass Leeranweisungen manchmal innerhalb von Schleifen oder anderen Spezialfällen verwendet werden können.

Hinweis: Obwohl dieses abschließende Semikolon keinen Zweck erfüllt, neige ich dazu es zu verwenden, und empfehle Ihnen es auch zu tun. Manchmal, nachdem Sie einige Zeilen geschrieben haben, kann es vorkommen, dass Sie eine weitere Anweisung hinzufügen möchten. Wenn das letzte Semikolon fehlt, so müssen Sie sich erinnern, es einzufügen. Daher ist es besser, es gleich zu tun.

Zuweisungen

Zuweisungen in Pascal verwenden den Doppelpunkt-Gleichheitszeichen-Operator (:=), eine ungewöhnliche Schreibweise für Programmierer, die bisher andere Sprachen verwendet haben. Der = Operator, welcher in einigen anderen Sprachen für die Zuweisung Verwendung findet, wird in Pascal für den Test auf Gleichheit benutzt.

Hinweis: Durch die Verwendung von unterschiedlichen Symbolen für die Zuweisung und den Gleichheitstest kann der Pascal-Compiler (genau wie der C-Compiler) den Quellcode schneller übersetzen, da er nicht den Zusammenhang untersuchen muß, in welchem der Operator verwendet wird, um seine Bedeutung festzustellen. Die Verwendung von unterschiedlichen Operatoren führt auch dazu, dass der Code für den Programmierer einfacher zu lesen ist.

Bedingte Anweisungen

Eine bedingte Anweisung wird verwendet, um in Abhängigkeit eines bestimmten Tests eine oder auch gar keine der Anweisungen auszuführen, die diese enthält. Es gibt zwei Arten von bedingten Anweisungen: if-Anweisungen und case-Anweisungen

Die if-Anweisung

Die if-Anweisung kann verwendet werden, um eine Anweisung nur dann auszuführen, wenn eine bestimmte Bedingung erfüllt ist (if-then), oder um zwischen zwei unterschiedlichen Alternativen zu wählen (if-then-else). Die Bedingung wird durch einen Booleschen Ausdruck beschrieben. Ein einfaches Delphi-Beispiel wird demonstrieren, wie bedingte Anweisungen geschrieben werden. Erzeugen Sie zunächst eine neue Anwendung und setzen Sie zwei Kontrollkästchen und vier Schaltflächen auf das Formular. Ändern Sie nicht die Namen der Schaltflächen oder Kontrollkästchen sondern doppelklicken Sie auf jede Schaltfläche, um eine Behandlungsroutine das OnClick-Ereignis einzufügen. Hier ist eine einfache if-Anweisung für die erste Schaltfläche:

procedure TForm1.Button1Click(Sender: TObject);
begin
  // einfache if-Anweisung
  if CheckBox1.Checked then
    ShowMessage ('CheckBox1 ist markiert')
end;

Wenn Sie auf die Schaltfläche klicken und das erste Kontrollkästchen markiert ist so wird das Programm eine einfaches Nachrichtenfenster anzeigen (vergl. Abbildung 5.1). Ich habe die Funktion ShowMessage verwendet, da diese die einfachste Delphi-Funktion ist, die Sie verwenden können, um dem Anwender eine kurze Nachricht anzuzeigen.

Abbildung 5.1: Die angezeigte Nachricht, die durch das Beispiel IfTest angezeigt wird, wenn Sie die erste Schaltfläche drücken und das erste Kontrollkästchen markiert ist.

Wenn Sie die Schaltfläche anklicken und nichts passiert, so liegt die Ursache darin, dass das Kontrollkästchen nicht markiert war. In einem Fall wie diesem würde es wahrscheinlich besser sein, dies auch zu berücksichtigen. So geschehen im Code für die zweite Schaltfläche, welcher eine if-then-else Anweisung verwendet:

procedure TForm1.Button2Click(Sender: TObject);
begin
  // if-then-else Anweisung
  if CheckBox2.Checked then
    ShowMessage ('CheckBox2 ist markiert')
  else
    ShowMessage ('CheckBox2 ist NICHT markiert');
end;

Beachten Sie, dass Sie kein Semikolon nach der ersten Anweisung und vor dem Schlüsselwort else einfügen können. Andernfalls wird der Compiler eine Fehlermeldung ausgeben. Die if-then-else Anweisung bildet tatsächlich eine einzelne Anweisung. Daher können Sie kein Semikolon innerhalb der Anweisung plazieren.

Eine if-Anweisung kann ziemlich komplex sein. Die Bedingung kann in einer Reihe von Bedingungen umgewandelt sein (unter Verwendung der Booleschen Operatoren and, or und not), oder die if-Anweisung kann mit einer zweiten if-Anweisung verschachtelt sein. Die letzten beiden Schaltflächen des IfTest-Beispiels demonstrieren diese Fälle:

procedure TForm1.Button3Click(Sender: TObject);
begin
  // Anweisung mit einer doppelten Bedingung
  if CheckBox1.Checked and CheckBox2.Checked then
    ShowMessage ('Beide Kontrollkästchen sind markiert')
end;

procedure TForm1.Button4Click(Sender: TObject);
begin
  // verschachtelte if-Anweisung
  if CheckBox1.Checked then
    if CheckBox2.Checked then
      ShowMessage ('CheckBox1 und 2 sind markiert')
    else
      ShowMessage ('Nur CheckBox1 is markiert')
  else
    ShowMessage (
      'Checkbox1 ist nocht markiert, wer kümmert sich um Checkbox2?')
end;

Sehen Sie sich den Code sorgfältig an und starten Sie das Programm, um zu sehen ob Sie alles verstanden haben. Wenn Sie über einem Programmkonstrukt im Zweifel sind, so können durch das Schreiben eines sehr einfachen Programms wie diesem Ihnen helfen, eine Menge zu lernen. Sie können weitere Kontrollkästchen hinzufügen und die Komplexität dieses kleinen Beispiels erhöhen, um nach Belieben zu testen.

Case-Anweisungen

Wenn Ihre if-Anweisung sehr komplex wird können Sie sie manchmal durch eine case-Anweisung ersetzen. Eine case-Anweisung besteht aus einem Ausdruck zur Ermittlung eines Wertes (dem Selektor) und einer Liste möglicher Werte oder Wertebereiche. Diese Werte sind sind Konstanten und sie müssen eindeutig sein sowie einen Aufzählungstyp darstellen. Optional kann eine else-Anweisung angegeben werden, die ausgeführt wird, wenn keine der Werte in der Liste mit dem Wert des Selektors übereinstimmt. Hier sind zwei einfache Beispiele:

case Number of
  1: Text := 'Eins';
  2: Text := 'Zwei';
  3: Text := 'Drei';
end;

case MyChar of
  '+' : Text := 'Vorzeichen Plus';
  '-' : Text := 'Vorzeichen Minus';
  '*', '/': Text := 'Multiplikation oder Division';
  '0'..'9': Text := 'Nummer';
  'a'..'z': Text := 'kleines Zeichen';
  'A'..'Z': Text := 'großes Zeichen';
else
  Text := 'unbekanntes Zeichen';
end;

Schleifen in Pascal

Die Sprache Pascal hat die typischen Wiederholungsanweisungen der meisten Programmiersprachen einschließlich der Anweisungen for, while und repeat. Das Verhalten dieser Schleifen wird Ihnen allgemein bekannt vorkommen, wenn Sie bereits andere Programmiersprachen verwendet haben. Daher werde ich sie nur kurz behandeln.

Die for-Schleife

Die for-Schleife in Pascal basiert streng auf einem Zähler, welcher jedesmal wenn die Schleife ausgeführt wird, wahlweise erhöht oder verringert wird. Hier ist ein Beispiel einer for-Schleife, die dazu dient die ersten zehn Zahlen zu addieren:

var
  K, I: Integer;
begin
  K := 0;
  for I := 1 to 10 do
    K := K + I;

Die gleiche for-Schleife hätte auch unter Verwendung eines Rückwärtszählers geschrieben werden können:

var
  K, I: Integer;
begin
  K := 0;
  for I := 10 downto 1 do
    K := K + I;

Die for-Schleife in Pascal ist weniger flexibel als in anderen Sprachen (es ist nicht möglich eine andere Schrittweite als eins anzugeben), sie ist aber einfach und leicht zu verstehen. Wenn Sie auf eine komplexere Bedingung testen müssen oder einen anwendungsspezifischen Zähler bereitstellen wollen, so müssen eine while- oder repeat-Anweisung an Stelle einer for-Schleife verwenden.

Hinweis: Der Zähler einer for-Schleife muss keine Zahl sein. Er kann ein Wert eines beliebigen Aufzählungstyps sein, wie etwa ein Zeichen oder ein selbstdefinierter Aufzählungstyp.

While- und repeat-Anweisungen

Der Unterschied zwischen der while-do Schleife und der repeat-until Schleife besteht darin, dass der Code der repeat-Anweisung stets mindestens einmal ausgeführt wird. Warum das so ist können Sie leicht verstehen, wenn Sie sich dieses einfache Beispiel ansehen:

while (I <= 100) and (J <= 100) do
begin
  // benutzen Sie I und J, um etwas zu berechnen...
  I := I + 1;
  J := J + 1;
end;

repeat
  // benutzen Sie I und J, um etwas zu berechnen...
  I := I + 1;
  J := J + 1;
until (I > 100) or (J > 100);

Auch wenn der Anfangswert von I oder J größer als 100 ist, werden die Anweisungen innerhalb der repeat-until Schleife dennoch einmal ausgeführt.

Der andere wesentliche Unterschied zwischen diesen beiden Schleifen besteht darin, dass die repeat-until Schleife eine umgekehrte Bedingung besitzt. Die Schleife wird so lange ausgeführt wie die Bedingung nicht erfüllt ist. Wenn die Bedingung erfüllt ist wird die Schleife beendet. Das ist das Gegenteil zur while-do Schleife, die so lange ausgeführt wird wie die Bedingung erfüllt ist. Aus diesem Grund musste ich die Bedingung im obigen Code umkehren, um eine ähnliche Anweisung zu erhalten.

Ein Beispiel zu Schleifen

Lassen Sie uns ein kleines Delphi-Beispiel ansehen, um die Details von Schleifen zu untersuchen. Das Schleifenprogramm beleuchtet den Unterschied zwischen einer Schleife mit einem festen Zähler und einer Schleife mit einem beinahe zufälligen Zähler. Beginnen Sie mit einem neuen leeren Projekt und platzieren Sie ein Listenfeld und zwei Schaltflächen auf dem Hauptformular. Geben Sie den Schaltflächen einen geeigneten Namen (BtnFor und BtnWhile) indem sie ihre Eigenschaft Name im Objektinspektor einstellen. Sie können auch das Wort Btn aus der Eigenschaft Caption entfernen (und eventuell sogar das &-Zeichen hinzufügen, um den nachfolgenden Buchstaben als Tastaturkürzel zu aktivieren). Hier eine Zusammenfassung der Textbeschreibung dieses Formulars:

object Form1: TForm1
  Caption = 'Loops'
  object ListBox1: TListBox ...
  object BtnFor: TButton
    Caption = '&For'
    OnClick = BtnForClick
  end
  object BtnWhile: TButton
    Caption = '&While'
    OnClick = BtnWhileClick
  end
end

Abbildung 5.2: Jedes mal wenn Sie die Schaltfläche For betätigen wird das Listenfeld mit aufeinander folgenden Zahlen gefüllt.

Nun können wir einigen Code für das Ereignis OnClick der beiden Schaltflächen einfügen. Wie Sie in Abbildung 5.2 sehen können, besitzt der erste Schalter eine einfache for-Schleife, um eine Liste von Zahlen anzuzeigen. Vor der Ausführung dieser Schleife, die eine Anzahl von Zeichenketten in die Eigenschaft Items des Listenfeldes anfügen, müssen Sie den Inhalt des Listenfeldes löschen:

procedure TForm1.BtnForClick(Sender: TObject);
var
  I: Integer;
begin
  ListBox1.Items.Clear;
  for I := 1 to 20 do
    Listbox1.Items.Add ('String ' + IntToStr (I));
end;

Der mit der zweiten Schaltfläche verbundene Code ist etwas komplizierter. In diesem Fall existiert hier eine while-Schleife, die auf einem Zähler basiert, der um einen zufälligen Wert erhöht wird. Um dies zu erreichen, habe ich die Prozedur Randomize, welche den Zufallszahlengenerator zurücksetzt, sowie die Funktion Random mit einem Wertebereich von 100 aufgerufen. Das Ergebnis dieser Funktion ist eine Zahl zwischen 0 und 99, die zufällig ausgewählt wird. Die Folge der Zufallszahlen steuert, wieviel mal die while-Schleife ausgeführt wird.

procedure TForm1.BtnWhileClick(Sender: TObject);
var
  I: Integer;
begin
  ListBox1.Items.Clear;
  Randomize;
  I := 0;
  while I < 1000 do
  begin
    I := I + Random (100);
    Listbox1.Items.Add ('Zufallszahl: ' + IntToStr (I));
  end;
end;

Jedes mal, wenn Sie die Schaltfläche While drucken, unterscheiden sich die Zahlen, da sie vom Zufallszahlengenerator abhängen. Abbildung 5.3 zeigt die Resultate nach zwei einzelnen Durchläufen. Beachten Sie, das nicht nur die erzeugten Zahlen jedesmal unterschiedlich sind, sondern auch die Anzahl der Zeilen. Das bedeutet, dass diese while-Schleife zufällig oft ausgeführt wird. Wenn Sie die Schaltfläche While mehrmals hintereinander drücken, so werden Sie sehen, dass das Listenfeld jedes mal eine unterschiedliche Anzahl von Zeilen enthält.

Abbildung 5.3: Der Inhalt des Listenfeldes im Beispiel Loops ändert sich jedes mal, wenn Sie die Schaltfläche While betätigen. Da der Schleifenzähler durch einen zufälligen Zahlenwert erhöht wird, wenn Sie den Schalter betätigen, läuft die Schleife jedes mal unterschiedlich oft ab.

Hinweis: Sie können den Standardablauf bei der Ausführung einer Schleife beeinflussen, indem Sie die Systemprozeduren Break und Continue verwenden. Die erste unterbricht die Schleife; die zweite wird verwendet, um direkt zum Schleifentest bzw. zur Zählererhöhung zu springen und mit der nächsten Iteration der Schleife fortzusetzen (es sei denn, die Abbruchbedingung ist erfüllt oder der Zähler hat seinen Endwert erreicht). Zwei weitere Systemprozeduren, Exit und Halt, lassen Sie unmittelbar aus der aktuellen Funktion oder Prozedur zurückkehren bzw. beenden das Programm.

Die with-Anweisung

Die letzte Art von Pascal-Anweisungen auf die ich eingehen möchte ist die with-Anweisung, die eine Besonderheit dieser Programmiersprache darstellt (und kürzlich auch in Visual Basic eingeführt wurde) und sehr nützlich bei der Delphi-Programmierung ist.

Die with-Anweisung ist nichts anderes als eine Kurzschreibweise. Wenn Sie sich auf eine Variable von einem Record-Typ (oder ein Objekt) beziehen müssen, so können Sie eine with-Anweisung verwenden, anstatt jedes mal seinen Namen zu wiederholen. Beispielsweise habe ich bei der Vorstellung des Record-Typs folgenden Code geschrieben:

type
  Date = record
    Year: Integer;
    Month: Byte;
    Day: Byte;
  end;

var
  BirthDay: Date;

begin
  BirthDay.Year := 1997;
  BirthDay.Month := 2;
  BirthDay.Day := 14;

Unter Verwendung der with-Anweisung kann ich den abschließenden Teil des Codes wie folgt verbessern:

begin
  with BirthDay do
  begin
    Year := 1995;
    Month := 2;
    Day := 14;
  end;

Diese Methode kann in Delphi-Programmen verwendet werden, um sich auf Komponenten und andere Klassentypen zu beziehen. Beispielsweise können wir den Schlussabschnitt aus dem letzen Beispiel, Loops, unter Verwendung einer with-Anweisung neu schreiben, um auf die Elemente des Listenfeldes zuzugreifen:

procedure TForm1.WhileButtonClick(Sender: TObject);
var
  I: Integer;
begin
  with ListBox1.Items do
  begin
    Clear; // Kurzschreibweise
    Randomize;
    I := 0;
    while I < 1000 do
    begin
      I := I + Random (100);
      // Kurzschreibweise:
      Add ('Zufallszahl: ' + IntToStr (I));
    end;
  end;
end;

Wenn Sie mit Komponenten oder Klassen allgemein arbeiten, gestattet Ihnen die with-Anweisung, einigen Code auszulassen, speziell für verschachtelte Felder. Beispielsweise können wir uns vorstellen, dass Sie die Breite und die Farbe des Zeichenstifts für ein Formular verändern wollen. Sie können den folgenden Code schreiben:

Form1.Canvas.Pen.Width := 2;
Form1.Canvas.Pen.Color := clRed;

Aber sicherlich ist dieser Code leichter zu schreiben:

with Form1.Canvas.Pen do
begin
  Width := 2;
  Color := clRed;
end;

Wenn Sie komplexen Code schreiben, so kann die with-Anweisung effektiv sein und Ihnen die Deklaration einiger temporärer Variablen ersparen, aber sie hat auch einen Nachteil. Sie kann den Code schwerer lesbar machen, besonders dann wenn sie mit unterschiedlichen Objekten arbeitet, die ähnliche oder korrespondierende Eigenschaften haben.

Ein weiterer Nachteil besteht darin, dass die with-Anweisung subtile logische Fehler im Code zulässt, die der Compiler nicht entdecken wird. Beispielsweise:

with Button1 do
begin
  Width := 200;
  Caption := 'Neue Beschriftung';
  Color := clRed;
end;

Dieser Code ändert die Beschriftung und die Breite des Schalters, aber er beeinflusst die Color-Eigenschaft des Formulars, nicht die des Schalters! Der Grund liegt darin, dass die Komponenten TButton keine Color-Eigenschaft besitzen und da der Code innerhalb eines Fomularobjekts ausgeführt wird (wir schreiben eine Methode des Formulars) so wird auf dieses Objekt standardmäßig zugegriffen. Wenn wir statt dessen geschrieben hätten:

Button1.Width := 200;
Button1.Caption := 'Neue Beschriftung';
Button1.Color := clRed; // Fehler!

würde der Compiler eine Fehlermeldung ausgeben. Im Allgemeinen können wir sagen: Da die with-Anweisung neue Bezeichner in den aktuellen Namensraum einführt, können wir damit existierende Bezeichner verdecken oder fälschlicherweise auf andere Bezeichner im selben Namensraum zugreifen (wie in der ersten Version des obigen Code-Fragments). Unter Berücksichtigung dieses Nachteils empfehle ich Ihnen die Verwendung der with-Anweisung, da sie sehr nützlich sein kann und teilweise die Lesbarkeit des Codes verbessert.

Sie sollten jedoch mehrfache with-Anweisungen vermeiden wie in:

with ListBox1, Button1 do...

Der nachfolgede Code wird wahrscheinlich sehr unleserlich, da Sie für jede Eigenschaft, die in diesem Block definiert ist, nachdenken müssen, welche Komponente gemeint ist, abhängig von den jeweiligen Eigenschaften und der Reihenfolge der Komponenten in der with-Anweisung.

Hinweis: Da wir gerade über die Lesbarkeit von Quelltexten reden -- Pascal besitzt keine endif- oder endcase-Anweisungen. Wenn eine if-Anweisung einen begin-end-Block besitzt, dann markiert das Ende des Blocks das Ende der Anweisung. Im Gegensatz dazu wird die case-Anweisung immer durch ein end beendet. Alle diese end-Anweisungen, häufig eine nach der anderen, können es schwierig machen, dem Code zu folgen. Nur durch Verfolgung der Einrückungen können Sie erkennen, auf welche Anweisung sich ein einzelnes end bezieht. Ein üblicher Weg um dies Problem zu lösen und den Code besser lesbar zu gestalten, besteht darin, einen Kommentar hinter der end-Anweisung anzufügen, der deren Funktion anzeigt, wie in:

if ... then
 ...
end; // if

Zusammenfassung

Dieses Kapitel beschrieb, wie bedingte Anweisungen und Schleifen kodiert werden. Anstatt jedoch lange Listen dieser Anweisungen zu schreiben werden Programme gewöhnlich in Routinen, Funktionen und Prozeduren, aufgeteilt. Dies ist das Thema des nächsten Kapitels, welches auch einige fortgeschrittene Elemente von Pascal vorstellt.

Nächstes Kapitel: Prozeduren und Funktionen

© Copyright Marco Cantù, Wintech Italia Srl 1995-2000
© Copyright der deutschen Übersetzung Immo Wache, 2000