Delphi / Object-Pascal : Seite 04

Einfache IF-Abfragen

Wir werden uns nun damit beschäftigen, unseren Taschenrechner aus der letzten Lektion etwas zu optimieren, insbesondere zur Vorbeugung vor Fehlern. So wird der Benutzer z.B. am Ende dieser Lektion gewarnt, dass, sofern er es versucht hat, eine Division durch Null nicht erlaubt ist. Diese Meldung soll in einem kleinen Dialogfenster ausgegeben werden, welches wir auch in dieser Lektion kennen lernen werden.

Kurzer Überblick über informationstechnische Logik

In der Informatik gibt es drei so genannte logische Operatoren, mit deren Hilfe man Bedingungen, die erfüllt sein sollen, damit etwas gemacht wird, verknüpfen kann. Diese Operatoren sind AND, OR und NOT, also UND, ODER und NICHT.
Schon aus dem Sprachgebrauch erkennen wir, dass auch keine anderen Operatoren notwendig sind. Jeden Wenn-Dann-Satz mit mehreren Bedingungen kann man mit diesen drei Operatoren bilden. Ein Beispiel:
WENN Wochenende ist ODER heute NICHT gearbeitet werden muss UND das Wetter gut ist, DANN gehe ich heute im Freibad schwimmen.
Von entscheidender Bedeutung ist beim Programmieren der "Satzaufbau", also die Reihenfolge der Bedingungen. Damit Delphi versteht, was es tun soll, setzt man Klammern:
WENN (Benutzername = abc UND Passwort = 123) ODER (Benutzername = def UND Passwort = 321), DANN lasse den Benutzer herein, SONST nicht.
Würde man ganz auf Klammern verzichten, so würde Delphi selbst Klammern setzen, und zwar so:
WENN ((Benutzername = abc UND Passwort = 123) ODER Benutzername = def) UND Passwort = 321, DANN lasse den Benutzer herein, SONST nicht.
Gehen wir jetzt mal davon aus, dass sich der Benutzer abc mit seinem Passwort 123 anmeldet. Ein Computer kennt grundsätzlich nur zwei Zustände: an und aus, also WAHR (true) oder FALSCH (false). Oben stünde dann:
WENN ((WAHR) ODER FALSCH) UND FALSCH, DANN lasse den Benutzer herein, SONST nicht.
Das bedeutet:
WENN (WAHR) UND FALSCH, DANN lasse den Benutzer herein, SONST nicht.
Und das bedeutet dann: WENN FALSCH, DANN lasse den Benutzer herein, SONST nicht.
FALSCH bedeutet hier, dass die Bedingung nicht erfüllt ist, obwohl der Benutzer den Benutzernamen und das Kennwort richtig eingegeben hat. Das würde nach einer Zeit für sehr böse Post seitens der Benutzer sorgen, die sich nicht anmelden konnten. Wir stellen also fest: die Logik einer Abfrage muss immer genau geprüft werden, bevor man sie endgültig aufnimmt in das Programm.

Wenn, dann und sonst: IF - THEN - ELSE

An dieser Stelle werden wir uns nochmals unseren Quelltext ansehen, der die Division in unserem Taschenrechner realisiert. Die Variable z1 wird durch z2 dividiert. Wenn nun aber z2 gleich Null ist, gibt Delphi von sich aus sofort eine Fehlermeldung aus, deren innerer Sinn zwar für Programmierer erkennbar ist, aber für den Benutzer weder von Relevanz noch von Verständnis.

Division beim Taschenrechner
procedure TForm1.Button4Click(Sender: TObject);
var z1, z2, ergebnis: real;
begin
  z1 := StrToFloat(Edit1.Text);
  z2 := StrToFloat(Edit2.Text);
  ergebnis := z1 / z2;
  Edit3.Text := FloatToStr(ergebnis);
end;
Wie kann man dem nun vorbeugen? Was wir brauchen ist eine Abfrage, die erkennt, dass z2 den Wert 0 enthält und je nach Abfrageergebnis (nicht vergessen: TRUE oder FALSE) auf die Eingabe des Benutzers reagiert. War die Eingabe Null, soll eine Fehlermeldung ausgegeben werden, sonst soll einfach das Ergebnis berechnet und ausgegeben werden. Wir werden damit auf diverse kleinere Probleme treffen, die wir aber sukzessive auslöschen werden. Beginnen wir mit der Grundlage unserer Abfrage: der IF-Anweisung. Der syntaktische Aufbau lautet:
Einfache IF-THEN-Abfrage
if { Bedingung } then { Anweisung };
Ein kleines Beispiel soll zeigen, wie die Abfrage funktionieren kann:
Anwendung einer einfachen IF-THEN-Abfrage
procedure TForm1.Button1Click(Sender: TObject);
begin
  if Edit1.Text = '123' then Edit2.Text := 'Passwort stimmt.';
end;
Wenn also im Editfeld Edit1 der Wert 123 steht, soll in das Editfeld Edit2 eingetragen werden, dass das Passwort stimmt. Wir sehen uns die Abfrage einmal genauer an. Den Teil mit IF hatten wir schon. Darauf folgt Edit1.Text = '123': fehlt da nicht der Doppelpunkt vor dem Gleichheitszeichen? Nein, denn wir wollen dem Feld doch nichts zuweisen, sondern das Feld vergleichen mit dem, was darin stehen soll, nämlich 123. Wir merken uns: die Kombination := ist ein Zuweisungsoperator, das einfache = ist ein Vergleichsoperator. Es gibt noch eine Reihe anderer Vergleichsoperatoren, z.B.:
Vergleichsoperatoren < kleiner als
> größer als
= gleich
<= kleiner oder gleich
>= größer oder gleich
In unserem Beispiel muss also geschaut werden, ob z2 = 0 ist. Es gibt nun zwei Möglichkeiten: entweder, wir prüfen, ob z2 Null ist, oder wir prüfen, ob z2 eben nicht Null ist. Je nach dem werden wir die Anweisungen eingeben. Aber vorher klären wir noch eine winzige Kleinigkeit: was, wenn der jeweils andere Fall eintritt? Etwa eine zweite IF-THEN-Abfrage? Das wäre doch sehr umständlich... hübsch wäre da doch so etwas wie eine Erweiterung, die es in der Sprache auch gibt: sonst. Und - wie sollte es anders sein - es gibt natürlich diese Option, und sie wird so angebaut:
Einfache IF-THEN-ELSE-Abfrage
if { Bedingung } then { Anweisung } else { andere Anweisung };
Übertragen auf das Beispiel von oben mit dem Passwort könnte man also schreiben:
Anwendung einer einfachen IF-THEN-ELSE-Abfrage
procedure TForm1.Button1Click(Sender: TObject);
begin
  if Edit1.Text = '123' then Edit2.Text := 'Passwort stimmt.' else Edit2.Text := 'Passwort stimmt NICHT.';
end;
Ist der Text in Edit1 nun nicht 123, wird in Edit2 festgestellt, dass das Passwort nicht stimmt. Kümmern wir uns nun aber um unseren Taschenrechner. Die Abfrage wird - in dieser Lektion - prüfen, ob z2 Null ist. Das würde dann so aussehen:
Abfrage für den Taschenrechner
procedure TForm1.Button1Click(Sender: TObject);
begin
  if z2 = 0 then { ... } else { ... }';
end;
Wenn z2 Null ist, dann soll ein Fehler ausgegeben werden. Dies werden wir mit einer kleinen Dialogbox regeln, einer so genannten ShowMessage-Dialogbox. Diese wird wie folgt aufgerufen:
ShowMessage
ShowMessage('Text, der ausgegeben werden soll');
Somit:
Abfrage für den Taschenrechner
procedure TForm1.Button1Click(Sender: TObject);
begin
  if z2 = 0 then ShowMessage('Die Division durch Null ist nicht erlaubt!') else { ... }';
end;
Kommen wir nun zum nächsten Problem: wir weisen, sofern z2 nicht Null ist, der Variablen ergebnis den Wert des Quotienten zu und lassen ihn danach ausgeben. Allerdings darf hinter else maximal eine Anweisung stehen. Da hilft uns nur eins: begin und end. Diese beiden Begriffe fungieren hier wieder wie eine Art Klammer, um zu zeigen, wo der else-Teil zuende ist. Der Quelltext für unsere Division wäre also insgesamt:
Division beim Taschenrechner
procedure TForm1.Button4Click(Sender: TObject);
var z1, z2, ergebnis: real;
begin
  z1 := StrToFloat(Edit1.Text);
  z2 := StrToFloat(Edit2.Text);
  if z2 = 0 then ShowMessage('Die Division durch Null ist nicht erlaubt!') else
  begin
    ergebnis := z1 / z2;
    Edit3.Text := FloatToStr(ergebnis);
  end;
end;
Was wäre passiert, wenn wir begin und end vergessen hätten? Der Quelltext wäre dann:
Division beim Taschenrechner
procedure TForm1.Button4Click(Sender: TObject);
var z1, z2, ergebnis: real;
begin
  z1 := StrToFloat(Edit1.Text);
  z2 := StrToFloat(Edit2.Text);
  if z2 = 0 then ShowMessage('Die Division durch Null ist nicht erlaubt!') else ergebnis := z1 / z2;
  Edit3.Text := FloatToStr(ergebnis);
  end;
end;
Da in ergebnis nichts steht, wenn z2 Null ist, würde auch in Edit3 Null oder etwas anderes stehen, was aber in jedem Falle falsch wäre, jedenfalls als Ergebnis. Es bleibt also bei der Version mit begin und end.

Einsatz logischer Operatoren

Wir wollen uns nun anhand einiger Beispiele vergegenwärtigen, wie wir Bedingungen mit den uns bekannten logischen Operatoren AND, OR und NOT verknüpfen können. Wir werden aber hier nicht ein Alltagsbeispiel verwenden, sondern einfach herumexperimentieren. Wir beginnen mit der AND-Verknüpfung.
Sie können dieses Beispiel mitverfolgen. Schließen Sie Delphi, wenn nicht schon geschehen, und öffnen Sie es erneut, um ein leeres Formular zu bekommen. Legen Sie sich auf dieses Formular ein Editfeld und einen Button, welchen Sie jetzt bitte doppelklicken, um seine OnClick-Routine zu ändern.
Das Programmierbeispiel wird sein, zu prüfen, ob die vom Benutzer eingegebene Zahl zwischen 5 und 100 liegt oder nicht. Man kann dieses Beispiel mit zwei IF-Abfragen realisieren, es geht aber auch mit einer, und in der Informatik gilt i.d.R. das Prinzip der Faulheit, d.h. eine Abfrage muss reichen.

Wir legen uns zunächst eine Variable an, die den eingegebenen Wert speichern soll. Wir nehmen für unser Beispiel aber nur ganze Zahlen, um nicht so viel Speicherplatz zu verschwenden. Ganze Zahlen heißen in Delphi Integer. Und das müssen wir auch bei der Variablendeklaration so schreiben:

Integer-Variable deklarieren
procedure TForm1.Button1Click(Sender: TObject);
var zahl: Integer;
begin
end;
Hinweis: Merken Sie sich bitte den Variablentypen Integer, er wird uns noch sehr häufig begegnen, insbesondere bei der Programmierung von Schleifen.
Nun legen wir fest, wie die Grenzen des zu prüfenden Bereiches aussehen. Wie gesagt wollen wir schauen, ob die Zahl zwischen 5 und 100 liegt. Wir legen uns dazu Konstanten an. Das geschieht mit dem Schlüsselwort const:
Konstanten deklarieren
procedure TForm1.Button1Click(Sender: TObject);
var zahl: Integer;
const
  untere_grenze = 5;
  obere_grenze = 100;
begin
end;
Der Vorteil von Konstanten ist, dass man keinen Typ angeben muss. Beachten Sie bitte, das die Wertzuweisung mittels eines einfachen Gleichheitszeichens geschieht, nicht mit dem Operator :=! Konstanten können während der Ausführung des Programms nicht mehr geändert werden, im Gegensatz zu Variablen.
Kümmern wir uns nun um die Zuweisung des Feldinhalts in die Variable zahl. Wie gewohnt wird dies folgendermaßen realisiert:
Zuweisung an zahl
procedure TForm1.Button1Click(Sender: TObject);
var zahl: Integer;
const
  untere_grenze = 5;
  obere_grenze = 100;
begin
  zahl := StrToInt(Edit1.Text);
end;
Nicht vergessen: zahl ist eine Integervariable! Die Zuweisung zahl := StrToFloat(...) wäre hier falsch, weil wir vom Typen String in den Typen Integer umwandeln müssen. Das geht durch zahl := StrToInt(...). Es gilt immer:
{Variable vom Typen 2} := {1}To{2}({Variable vom Typen 1});
Nachdem wir nun die Variable initialisiert, d.h. belegt haben, können wir damit auch arbeiten. Wir wollen, dass eine Nachricht ausgegeben wird, wenn die Zahl größer als fünf und kleiner als 100 ist, sonst soll eine negative Nachricht ausgegeben werden. Und das realisieren wir so:
IF-Abfrage mit AND
procedure TForm1.Button1Click(Sender: TObject);
var zahl: Integer;
const
  untere_grenze = 5;
  obere_grenze = 100;
begin
  zahl := StrToInt(Edit1.Text);
  if (zahl > untere_grenze) and (zahl < obere_grenze)
  then showMessage('Zahl liegt dazwischen.')
  else showMessage('Zahl liegt nicht dazwischen.');
end;
Sie werden sich vielleicht fragen, ob es möglich ist, das Programm so zu schreiben wie oben gezeigt, also mitten in der Anweisung eine neue Zeile zu nutzen. Delphi ist es egal, ob in einem Befehl eine neue Zeile begonnen wird, solange die Syntax, d.h. der Aufbau stimmt. Am Ende des Befehls wird daher auch ein Semikolon geschrieben, damit Delphi weiß, dass der Befehl dort endet.
Wenn wir das Programm nun starten und z.B. 6 eingeben, kommt als Meldung (bei Klick auf den Button), dass die Zahl zwischen 5 und 100 liegt. Geben wir 2 oder 107 ein, kommt die Meldung, dass es nicht der Fall ist. Was wäre nun aber passiert, wenn wir statt AND ein OR eingegeben hätten, der Quelltext also dieser wäre:
IF-Abfrage mit OR statt AND
procedure TForm1.Button1Click(Sender: TObject);
var zahl: Integer;
const
  untere_grenze = 5;
  obere_grenze = 100;
begin
  zahl := StrToInt(Edit1.Text);
  if (zahl > untere_grenze) or (zahl < obere_grenze)
  then showMessage('Zahl liegt dazwischen.')
  else showMessage('Zahl liegt nicht dazwischen.');
end;
Die Antwort ist ganz einfach: egal, welche Zahl man eingibt, es würde immer angezeigt werden, dass sie zwischen 5 und 100 liegt, weil jede Zahl größer als 5 oder kleiner als 100 ist. Eine Zahl, auf die das nicht zutrifft, gibt es nicht. Auch die 5 und die 100 selbst sind davon betroffen.
Versuchen Sie nun, zu erraten, was die folgende Änderung auswirken würde:
Abgeänderte IF-Abfrage mit OR statt AND
procedure TForm1.Button1Click(Sender: TObject);
var zahl: Integer;
const
  untere_grenze = 5;
  obere_grenze = 100;
begin
  zahl := StrToInt(Edit1.Text);
  if (zahl > untere_grenze) or not (zahl < obere_grenze)
  then showMessage('Zahl liegt dazwischen.')
  else showMessage('Zahl liegt nicht dazwischen.');
end;
Lösung: Alle Zahlen, die größer als 5 sind, werden die Meldung Zahl liegt dazwischen. ausgeben, alle kleiner/gleich 5 rufen Zahl liegt nicht dazwischen. auf. An dieser Stelle muss ich aber wieder auf das Prinzip der Faulheit in der Informatik verweisen: die Bedingung not (zahl < obere_grenze) ist anders formuliert nichts weiter als (zahl >= obere_grenze).

Aufgaben

4.1 Einfache IF-Abfragen

Lektion 03