10. Bedingungen
10.1 Einfache Auswahl: IF
… THEN
10.1.1 Einzeilige Bedingungen
Während das Computerprogramm zunächst einmal der Reihe nach von der ersten Zeile bis zur letzten abgearbeitet wird, ist es häufig nötig, bestimmte Programmteile nur unter besonderen Voraussetzungen auszuführen. Denken Sie an folgendes Beispiel aus dem Alltag: Wenn es acht Uhr abends ist — und nur dann — soll der Fernseher für die Nachrichten eingeschalten werden. (Ja, es soll tatsächlich Haushalte geben, in denen der Fernseher nicht den ganzen Tag über läuft …)
Wenn es 20:00 Uhr ist Dann schalte den Fernseher ein.
Zunächst tauchen in dem Satz die beiden Schlüsselwörter Wenn
und Dann
auf. Zwischen den beiden Wörtern steht eine Bedingung (es ist 20:00 Uhr
). Diese Bedingung kann erfüllt sein oder nicht — aber nur dann, wenn sie erfüllt ist, wird die anschließende Handlung (schalte den Fernseher ein
) durchgeführt.
In FreeBASIC-Syntax sieht das ähnlich aus:
IF bedingung THEN anweisung
Nehmen wir einmal ein einfaches Beispiel: Der Benutzer wird nach seinem Namen gefragt. Wenn dem Programm der Name besonders gut gefällt, findet es dafür lobende Worte.
DIM AS STRING eingabe INPUT "Gib deinen Namen ein: ", eingabe IF eingabe = "Stephan" THEN PRINT "Das ist aber ein besonders schoener Name!" PRINT "Danke, dass du dieses Programm getestet hast." SLEEP
Die Ausgabe "Das ist aber ein besonders schoener Name!" erscheint nur, wenn die zuvor genannte Bedingung erfüllt ist.
10.1.2 Mehrzeilige Bedingungen (IF
-Block)
Nun passiert es häufig, dass unter einer bestimmten Bedingung nicht nur ein einziger Befehl, sondern eine ganze Reihe von Anweisungen ausgeführt werden soll. Alle Anweisungen in eine Zeile zu quetschen, wird schnell unübersichtlich. Wenn daher auf das THEN
keine Anweisung, sondern ein Zeilenumbruch erfolgt, bedeutet das für den Compiler, dass die folgenden Zeilen nur unter der gegebenen Bedingung ausgeführt werden sollen. Allerdings muss dann explizit festgelegt werden, wo dieser Anweisungsblock enden soll. Dazu dient der Befehl END IF
.
In „Alltagssprache“ könnte das etwa so aussehen:
Wenn du Hunger hast Dann Oeffne Keksdose Nimm Keks Schliesse Keksdose Iss Keks Ende Wenn
Für die FreeBASIC-Umsetzung bauen wir Quelltext 10.1 ein klein wenig um:
DIM AS STRING eingabe INPUT "Gib deinen Namen ein: ", eingabe IF eingabe = "Stephan" THEN PRINT "Das ist aber ein besonders schoener Name!" ' Hier koennten jetzt noch weitere Befehle folgen END IF PRINT "Danke, dass du dieses Programm getestet hast." SLEEP
Diese Variante wird auch als IF
-Block bezeichnet. Natürlich kann sie auch genutzt werden, wenn nur eine einzige Anweisung ausgeführt werden soll. Manche Programmierer bevorzugen es, IF
-Abfragen ausschließlich mit Blöcken umzusetzen, weil dadurch Bedingungsabfrage und resultierende Anweisung klar voneinander getrennt sind. Dies kann sich positiv auf die Lesbarkeit des Quelltextes auswirken.
Apropos Lesbarkeit: Sicher sind Ihnen die Einrückungen in den beiden letzten Beispielen aufgefallen. Solche Einrückungen spielen für den Compiler keine Rolle; ihm ist es völlig egal, ob Sie Ihre Zeilen einrücken und wie weit. Wichtig sind sie jedoch für den Leser des Quelltextes — also für alle, welche die Funktionsweise des Programmes verstehen und vielleicht auch verbessern und weiterentwickeln wollen, und natürlich für den Programmierer selbst!
Um den Quelltext lesbar zu gestalten, werden alle Zeilen, die auf derselben „logischen Ebene“ stehen, gleich weit eingerückt. In Quelltext 10.2 werden die ersten drei Zeilen immer ausgeführt (auch die Bedingungsabfrage findet immer statt). Daher befinden sie sich auf der „obersten“ Ebene, sind also nicht eingerückt. Zeilen 4 und 5 werden nur unter der genannten Bedingung ausgeführt. Sie befinden sich eine Ebene „tiefer“ und werden eingerückt. Da sie sich beide auf derselben Ebene befinden — ist die Bedingung erfüllt, werden beide Zeilen berücksichtigt — wurden sie auch gleich weit eingerückt. END IF
beendet den IF
-Block und steht damit wieder in derselben Einrückungsebene wie Zeile 3, ebenso wie die restlichen Zeilen, die unabhängig von der Bedingung in jedem Fall ausgeführt werden.
Wie stark die Zeilen eingerückt werden, wird von Programmierer zu Programmierer unterschiedlich gehandhabt. Üblich sind Einrückungen von zwei bis vier Leerzeichen pro Ebene oder, alternativ dazu, die Verwendung von Tabulatoren. Bleiben Sie jedoch innerhalb einer Quelltext-Datei bei einer dieser Möglichkeiten: verwenden Sie entweder immer zwei Leerzeichen oder immer vier Leerzeichen oder immer Tabulatoren. Die Arten zu mischen sorgt lediglich für Verwirrung, da man dann nicht mehr auf den ersten Blick einwandfrei die Ebenentiefe erkennen kann. Leerzeichen und Tabulatoren zu mischen ist sowieso eine schlechte Idee, allein schon, weil unterschiedliche Editoren einen Tabulator auch unterschiedlich weit eingerückt darstellen können. Was in Ihrem Editor dann gleichmäßig aussieht, ist bei einem anderen möglicherweise sehr ungleichmäßig.
Ein sauberer Umgang mit Einrückungen ist vor allem wichtig, wenn mehrere Blöcke ineinander verschachtelt sind:
DIM username AS STRING, alter AS INTEGER INPUT "Gib deinen Namen ein: ", username IF username = "Stephan" THEN PRINT "Das ist aber ein besonders schoener Name!" INPUT "Wie alt bist du? ", alter IF alter = 0 THEN PRINT "Dann bist du ja vor nicht einmal einem Jahr geboren worden!" END IF END IF PRINT "Danke, dass du dieses Programm getestet hast." SLEEP
Die Benutzereingabe des Alters und die daraus resultierende Bedingungsabfrage erfolgen nur, wenn bereits der richtige Name eingegeben wurde. Ansonsten springt das Programm schon von vornherein in die Zeile 10. IF
-Blöcke können beliebig ineinander verschachtelt werden. Jedoch muss jeder IF
-Block auch mit einem END IF
beendet werden.
Hinweis: Statt END IF findet man in manchen Quelltexten auch die zusammengeschriebene Form ENDIF . Diese wird in einigen anderen BASIC-Dialekten für den Abschluss des Blockes verwendet und wird in FreeBASIC aus Kompatibilitätsgründen ebenso unterstützt. |
10.1.3 Alternativauswahl
In der bisherigen einfachen Form wird der Code nur ausgeführt, wenn die Bedingung erfüllt ist; ansonsten wird das Programm nach dem END IF
fortgesetzt. Man kann allerdings auch Alternativen festlegen, die nur dann ausgeführt werden, wenn die Bedingung nicht erfüllt ist. Dazu dienen die beiden Schlüsselwörter ELSEIF
und ELSE
.
IF bedingung1 THEN anweisungen ELSEIF bedingung2 THEN anweisungen ELSEIF bedingung3 THEN anweisungen ' ... ELSE anweisungen END IF
Zuerst wird bedingung1 geprüft. Ist sie erfüllt, werden die Anweisungen nach dem THEN
ausgeführt. Die ELSEIF
-Blöcke werden in diesem Fall übersprungen. Ist bedingung1 dagegen nicht erfüllt, werden der Reihe nach die Bedingungen hinter den ELSEIF
-Zeilen geprüft, bis das Programm auf die erste zutreffende Bedingung stößt. Der darauf folgende Anweisungsblock wird dann ausgeführt. Die Anweisungen nach dem ELSE
kommen nur dann zum Tragen, wenn keine einzige der abgearbeiteten Bedingungen erfüllt war.
Ein IF
-Block kann beliebig viele (auch keine) ELSEIF
-Zeilen enthalten, aber höchstens eine ELSE
-Zeile.
DIM AS INTEGER alter INPUT "Gib dein Alter ein: ", alter IF alter < 0 THEN ' Das Alter ist kleiner als 0 PRINT "Das Alter kann nicht negativ sein!" ELSEIF alter < 3 THEN ' Das Alter ist mind. 0, aber kleiner als 3 PRINT "Bist du nicht noch zu jung?" ELSEIF alter < 18 THEN ' Das Alter ist mind. 3, aber kleiner als 18 PRINT "Du bist noch nicht volljaehrig!" ELSE ' Das Alter ist mind. 18 PRINT "Dann bist du ja volljaehrig!" END IF SLEEP
Wenn Sie Quelltext 10.4 mehrmals mit verschiedenen Alterseingaben ausführen, werden Sie sehen, dass immer nur der erste Abschnitt ausgeführt wird, dessen Bedingung erfüllt ist.
10.2 Bedingungsstrukturen
Bedingungen können, wie gesagt, wahr oder falsch sein. Nun würde sich zur Verwaltung von Wahrheitswerten natürlich ein BOOLEAN
anbieten. FreeBASIC steht hier jedoch stark in der Tradition alter BASIC-Dialekte, die es ermöglichen, Wahrheitswerte auch in aufwändige Rechnungen einzubauen. Daher verwendet FreeBASIC — wie schon sein Vorgänger QuickBASIC — für Vergleiche die Ergebnisse 0
für falsch und -1
für wahr. Allerdings wird jeder beliebige Zahlenwert ungleich 0
als wahr interpretiert. Beispielsweise würde auch dies hier funktionieren:
IF 2+3 THEN PRINT "Der Ausdruck '2+3' ist wahr."
2+3
ist nicht 0
, und daher wird der anschließende Text angezeigt. Aus diesem Grund wird in diesem Buch zwischen dem Wahrheitswert wahr und dem BOOLEAN
true
unterschieden. Nichtsdestotrotz kann der Wert eines Vergleiches (0
oder -1
) auch einem BOOLEAN
zugewiesen und damit in einen der Werte false
oder true
konvertiert werden.
10.2.1 Vergleiche
Bisher haben wir einfache Vergleiche wie =
(ist gleich) und <
(ist kleiner als) verwendet. Dass bei beiden Vergleichen Zahlenwerte zurückgegeben werden, kann man übrigens leicht mithilfe von PRINT
überprüfen:
PRINT 5 = 5 ' offensichtlich wahr; also wird -1 ausgegeben PRINT 5 < 5 ' falsch; die Ausgabe ist 0 PRINT 6-2*(5=5) ' Rechenausdruck mit dem Ergebnis 8 SLEEP
Folgende Vergleichsoperatoren können verwendet werden:
Operator | Bedeutung | Beispiel | Wert |
---|---|---|---|
> |
größer als |
5 > 5 |
|
3 > 8 |
|
||
< |
kleiner als |
5 < 5 |
|
3 < 8 |
|
||
= |
gleich |
5 = 5 |
|
3 = 8 |
|
||
>= |
größer oder gleich |
5 >= 5 |
|
3 >= 8 |
|
||
<= |
kleiner oder gleich |
5 <= 5 |
|
3 <= 8 |
|
||
<> |
ungleich |
5 <> 5 |
|
3 <> 8 |
|
Tabelle 10.1: Liste der Vergleichsoperatoren
Nicht nur Zahlen können miteinander verglichen werden, sondern auch Zeichenketten. Während die Prüfung auf Gleichheit bzw. auf Ungleichheit noch sehr einleuchtend ist, benötigen die Operatoren > und < bei Zeichenketten noch eine genauere Erläuterung. Die Zeichenketten werden hierbei von vorn nach hinten Zeichen für Zeichen verglichen (solange bis das Ergebnis feststeht). Ausschlaggebend ist dabei der ASCII-Code des Zeichens (vgl. dazu Kapitel 13.4 und Anhang C). Zu beachten ist dabei, dass Großbuchstaben einen kleineren ASCII-Code besitzen als Kleinbuchstaben.
PRINT "Ist 'b' kleiner als 'a'? "; "b" < "a" PRINT "Ist 'Anna' kleiner als 'Anne'? "; "Anna" < "Anne" PRINT "Ist 'Kind' kleiner als 'Kinder'? "; "Kind" < "Kinder" PRINT "Ist 'Z' kleiner als 'a'? "; "Z" < "a" SLEEP
Ist 'b' kleiner als 'a'? 0
Ist 'Anna' kleiner als 'Anne'? -1
Ist 'Kind' kleiner als 'Kinder'? -1
Ist 'Z' kleiner als 'a'? -1
Gerade das letzte Beispiel scheint den Vergleichsoperator für eine alphabetische Sortierung (für die Programmierung eines Telefonbuchs o. ä.) ungeeignet zu machen. Doch dafür gibt es Abhilfe: Die zu vergleichenden Zeichenketten können zuvor komplett in Kleinbuchstaben (oder alternativ natürlich auch komplett in Großbuchstaben) umgewandelt werden. Darauf werden wir in Kapitel 15.2.6 zu sprechen kommen.
Für Fortgeschrittene: Die Vergleichsoperatoren überprüfen in FreeBASIC lediglich die Gleichheit der Werte. Die Gleichheit der Datentypen kann z. B. mit TYPEOF() (zur Compilier-Zeit) oder IS (bei UDTs mit RTTI-Funktionalität) geprüft werden. |
10.2.2 Logische Operatoren
Bedingungen setzen sich manchmal aus mehreren Teilaspekten zusammen. Im folgenden Beispiel wird der Zutritt ab 18 Jahren gestattet. Jugendliche dürfen nur eintreten, wenn sie mindestens 14 Jahre alt sind und von ihren Eltern begleitet werden.
Wenn Alter >= 18 Oder (Alter > 13 Und Begleitung = "Eltern") Dann Zutritt
In FreeBASIC heißen die entsprechenden Operatoren OR
(oder) bzw. AND
(und). Wenn Sie in einer Bedingung beide Operatoren mischen, sollten Sie zusammengehörige Ausdrücke wie im obigen Beispiel mit Klammern umschließen, um sicher zu gehen, dass sie in der richtigen Reihenfolge ausgewertet werden. Wären die Klammern anders gesetzt, wäre auch eine andere Bedingung gefordert.
In FreeBASIC-Syntax sieht die Bedingung folgendermaßen aus:
IF Alter >= 18 OR (Alter > 13 AND Begleitung = "Eltern") THEN Zutritt
Hinweis: Es gibt bei der Abarbeitung der Operatoren eine feste Reihenfolge, ähnlich wie die Rechenvorschrift „Punkt vor Strich“. Sie wird durch die sogenannten Vorrangregeln bestimmt, die in Anhang F aufgelistet sind, und kann durch den Einsatz von runden Klammern geändert werden. Wenn Sie unsicher sind, welche Operatoren Vorrang haben, sollten Sie auf Klammern zurückgreifen, auch um eine bessere Lesbarkeit des Quelltextes zu gewährleisten. |
AND
prüft also, ob beide Bedingungen (links und rechts vom AND
) wahr sind, während OR
überprüft, ob mindestens eine der beiden Bedingung zutrifft. Ganz richtig ist das allerdings noch nicht — verglichen werden nämlich die einzelnen Bits der beiden Ausdrücke. Dazu ist es wichtig zu wissen, dass der Computer im Binärsystem rechnet.
10.2.3 Das Binärsystem
Werte werden im Prozessor durch zwei Zustände (z. B. Strom an/Strom aus) festgehalten; im übertragenen Sinn kann man von zwei Werten 0 und 1 sprechen. Dies entspricht einer Speichereinheit bzw. 1 Bit. 8 Bit werden zusammengefasst zu 1 Byte, welches 28 = 256 Zustände annehmen kann, je nachdem, welche Bit gesetzt sind und welche nicht.
Ein möglicher Zustand wäre z. B. folgender:
Bit 7 |
Bit 6 |
Bit 5 |
Bit 4 |
Bit 3 |
Bit 2 |
Bit 1 |
Bit 0 |
0 |
1 |
0 |
0 |
1 |
0 |
1 |
1 |
Wird dieser Zustand als Ganzzahl interpretiert, dann zählt von rechts nach links jedes Bit doppelt so viel wie das davor. Bit 0 zählt also als 1
, Bit 1 als 2
, Bit 2 als 4
usw. Dies entspricht im wesentlichen der Darstellung einer Zahl im Dezimalsystem, nur dass die einzelnen Stellen nicht mehr Einer, Zehner, Hunderter, Tausender usw. repräsentieren, sondern Einer, Zweier, Vierer, Achter usw. Die oben dargestellte Zahl hätte also (von rechts gelesen) den Wert
1 · 1 + 1 · 2 + 0 · 4 + 1 · 8 + 0 · 16 + 0 · 32 + 1 · 64 + 0 · 128 = 75
Man spricht hierbei vom Binärsystem oder, mathematisch etwas genauer, vom Dualsystem (Programmierer verwenden beide Begriffe synonym). Dass die Nummerierung der Bits mit 0
beginnt, hat übrigens einen ganz praktischen Grund, denn dadurch lässt sich die Wertigkeit eines Bits ganz einfach als Zweierpotenz berechnen: 20 = 1, 21 = 2, 22 = 4
, …
Mit diesem System können mit einem Byte Zahlen von 0 bis 255 dargestellt werden. Für größere Zahlen werden mehrere Byte „zusammengesetzt“. Auch negative Zahlen können so dargestellt werden — dazu wird vereinbart, dass das höchstwertige Bit kennzeichnet, ob die Zahl positiv oder negativ ist. Für den Wert -1
sind alle Bit gesetzt, für -2
alle außer Bit 0
usw. In Quelltext 10.6 wird die Funktion BIN()
verwendet, um die Binärdarstellung von Dezimalzahlen zu erhalten. Sie können etwas experimentieren, wie negative Werte binär dargestellt werden.
Hintergrundinformation: Die von FreeBASIC verwendete Darstellung für Ganzzahlen heißt Zweierkomplementdarstellung. Es ist die heute am häufigsten verwendete Art, positive und negative Zahlen in binärer Form darzustellen. |
Auch andere Informationen wie Farbwerte auf dem Monitor oder Zeichen innerhalb einer Zeichenkette werden letztendlich über das Binärsystem in Bytes gespeichert.
10.2.4 AND
und OR
als Bit-Operatoren
Auch wenn wir zuvor so getan haben, also ob es sich bei AND
und OR
um logische Operatoren handelt, ist das nicht ganz richtig. Sofern ausschließlich BOOLEAN
bzw. nur die Werte 0
und -1
verwendet werden, ist alles in Ordnung, allerdings verknüpfen die beiden Operatoren die Werte bitweise. Das bedeutet: Die Werte werden Bit für Bit miteinander verglichen und im Rückgabewert entsprechend gesetzt oder nicht. AND
setzt das Bit im Rückgabewert genau dann, wenn das Bit auch in beiden Operanden gesetzt ist. OR
setzt das Bit genau dann, wenn es in mindestens einem der Operanden gesetzt ist.
Das soll in folgendem Codebeispiel verdeutlicht werden. Dazu bedienen wir uns der Funktion BIN()
, die den Binärwert einer Zahl als String zurückgibt. BIN()
kann neben der umzuwandelnden Zahl optional noch ein zweiter Parameter mitgegeben werden, der angibt, wie viele Stellen der Binärzahl zurückgegeben werden sollen — ggf. wird die Zahl abgeschnitten oder mit Nullen aufgefüllt. Durch eine einheitliche Länge lässt sich die Ausgabe besser formatieren und damit leichter lesen. Im folgenden Quelltext werden alle Binärwerte mit vier Stellen ausgegeben.
DIM AS INTEGER z1 = 6, z2 = 10 PRINT "Verknuepfung AND" PRINT z1, BIN(z1, 4) PRINT z2, BIN(z2, 4) PRINT "---", "----" PRINT z1 AND z2, BIN(z1 AND z2, 4) PRINT "Verknuepfung OR" PRINT z1, BIN(z1, 4) PRINT z2, BIN(z2, 4) PRINT "---", "----" PRINT z1 OR z2, BIN(z1 OR z2, 4) SLEEP
Verknuepfung mit AND
6 0110
10 1010
--- ----
2 0010
Verknuepfung mit OR
6 0110
10 1010
--- ----
14 1110
Wenn Sie AND
zur Bestimmung eines Wahrheitswertes verwenden wollen, stoßen Sie hier auf ein Problem: die Zahlen 2
und 4
z. B. werden beide als wahr interpretiert, 2 AND 4
jedoch ist 0
und damit falsch. Sie sollten in diesem Fall unbedingt auf die Vergleichoperatoren =, < usw. zurückgreifen oder sicherstellen, dass nur die Werte 0
und -1
verglichen werden.
10.2.5 ANDALSO
und ORELSE
Alternativ dazu existieren die (nun tatsächlich logischen) Operatoren ANDALSO
und ORELSE
. Diese arbeiten jedoch etwas anders: Zunächst werden beide Seiten immer als Wahrheitswert definiert, womit 2 ANDALSO 4 = -1
(wahr) ist. Zum anderen wird die Auswertung nur so lange durchgeführt, bis das Ergebnis feststeht. In der Praxis bedeutet das für ANDALSO
: Wenn die linke Seite bereits falsch ist, kann der Gesamtausdruck nicht mehr wahr sein, und die rechte Seite wird daher gar nicht mehr ausgewertet. Wenn umgekehrt bei ORELSE
die linke Seite wahr ist, dann ist auch der Gesamtausdruck wahr. Auch hier wird die rechte Seite nicht mehr ausgewertet.
Ein einfaches Beispiel:
DIM AS INTEGER z1 = 3, z2 = 5 IF z1 < 2 ANDALSO z2 > 4 THEN PRINT "Hoppla ..." SLEEP
Wenn beide Operanden vom Typ BOOLEAN
sind, liefern ANDALSO
und ORELSE
als Rückgabewert ein BOOLEAN
, ansonsten einen der Werte -1
und 0
. Wenn Sie die Operatoren allerdings nur zur Bedingungsabfrage einsetzen und nicht innerhalb von Rechenausdrücken, macht das keinen Unterschied.
Sinnvoll sind die beiden Operatoren z. B. dann, wenn auf der rechten Seite aufwendige Berechnungen durchgeführt werden müssen, die dann, wenn die linke Seite nicht erfüllt ist, komplett wegfallen können. Andererseits kann es auch passieren, dass die rechte Seite gar nicht ausgewertet werden darf, da die erforderlichen Bedingungen nicht erfüllt sind und die Auswertung zu einem Fehler führen würde. ANDALSO
findet man daher auch oft im Zusammenhang mit Pointerzugriffen, vor denen geprüft wird, ob der Pointer überhaupt korrekt gesetzt ist.
' Vermeidung der aufwendigen Berechnung, wenn Vorbedingung nicht erfuellt IF bedingung ANDALSO aufwendigeBerechnung() > 0 THEN tueWas ' Vermeidung eines unerlaubten Null-Pointer-Zugriffs IF meinPointer ANDALSO *meinPointer = 10 THEN tueWas
In beiden Fällen wird zunächst überprüft, ob der erste Ausdruck wahr ist, also ob bedingung
bzw. meinPointer
einen Wert ungleich Null besitzen. Ob meinPointer
tatsächlich ein korrekter Pointer auf eine Zahl ist, kann damit zwar nicht sichergestellt werden, aber zumindest wird so ein Nullpointer abgefangen. Die Abfrage des gespeicherten Wertes erfolgt auf jeden Fall nur dann, wenn meinPointer
kein Nullpointer ist.
Achtung:ANDALSO und ORELSE führen nicht automatisch zu einem schnelleren Code! Falsch angewendet kann es im Extremfall sogar zu einer Verlangsamung kommen. |
10.2.6 Weitere Bit-Operatoren: XOR
, EQV
, IMP
und NOT
Neben AND
und OR
wird auch der Operator XOR
(eXclusive OR) häufig verwendet, der umgangssprachlich als entweder — oder aufgefasst werden kann. Es muss also genau eines der verglichenen Bits gesetzt sein. Sind beide Bits gesetzt, ergibt sich der Wert 0
, genauso wenn keines der beiden Bits gesetzt ist.
Etwas exotischer sind die beiden Operatoren EQV
(EQuivalent Value) und IMP
(IMPlication). EQV
prüft die Gleichheit beider Bits, also ob beide gesetzt sind oder beide nicht. Bei IMP
ist der Hintergrund etwas komplizierter: Es wird geprüft, ob aus der ersten Aussage die zweite folgt. Praktisch bedeutet es, dass das Bit immer gesetzt wird, außer wenn es im linken Ausdruck gesetzt war, im rechten jedoch nicht.
NOT
schließlich dreht die Bitwerte einfach um: wo das Bit gesetzt war, ist es im Ergebnis nicht gesetzt und umgekehrt. Im Gegensatz zu den bisherigen Operatoren wird NOT
lediglich auf einen Operanden angewandt. NOT 0
z. B. ist -1
und NOT -1
ist 0
(auf den Gesamtwert betrachtet).
Noch einmal zusammengefasst: Die Bit-Operatoren AND
, OR
, XOR
, EQV
, IMP
und NOT
vergleichen die beiden übergebenen Ausdrücke (bei NOT
nur ein übergebener Ausdruck) Bit für Bit und setzen das entsprechende Bit im Ergebnis je nachdem auf den Wert 0
oder 1
. Tabelle 10.2 stellt das Verhalten der Operatoren in einer Übersicht zusammen.
Schlüsselwort | Ausdruck1 | Ausdruck2 | Ergebnis |
---|---|---|---|
AND |
0 |
0 |
0 |
0 |
1 |
0 |
|
1 |
0 |
0 |
|
1 |
1 |
1 | |
OR |
0 |
0 |
0 |
0 |
1 |
1 |
|
1 |
0 |
1 |
|
1 |
1 |
1 | |
XOR |
0 |
0 |
0 |
0 |
1 |
1 |
|
1 |
0 |
1 |
|
1 |
1 |
0 | |
EQV |
0 |
0 |
1 |
0 |
1 |
0 |
|
1 |
0 |
0 |
|
1 |
1 |
1 | |
IMP |
0 |
0 |
1 |
0 |
1 |
1 |
|
1 |
0 |
0 |
|
1 |
1 |
1 | |
NOT |
0 |
- |
1 |
1 |
- |
0 |
Tabelle 10.2: Bit-Operatoren
Sofern diese Operatoren ausschließlich für BOOLEAN
bzw. ausschließlich für -1
und 0
verwendet werden (aber nur dann), verhalten sie sich wie logische Operatoren.
10.3 Mehrfachauswahl: SELECT CASE
Wenn eine Variable auf mehrere verschiedene Werte geprüft werden soll, ist das Konstrukt IF … THEN … ELSEIF … ELSE … END IF
oft recht umständlich. Ein denkbarer Fall wäre eine Tastaturabfrage, auf die in Abhängigkeit von der gedrückten Taste reagiert werden soll. Im Folgenden soll auf die Tasten W-A-S-D bzw. 8-4-5-6 zur Steuerung einer Spielfigur reagiert werden. Da es egal sein soll, ob dabei die Shift-Taste gedrückt wurde oder nicht, wird der Tastenwert zuerst mit LCASE()
in Kleinbuchstaben umgewandelt.
DIM AS STRING taste PRINT "Druecke eine Steuerungstaste." taste = INPUT(1) IF LCASE(taste) = "w" or taste = "8" THEN PRINT "Taste nach oben gedrueckt" ELSEIF LCASE(taste) = "s" or taste = "5" THEN PRINT "Taste nach unten gedrueckt" ELSEIF LCASE(taste) = "a" or taste = "4" THEN PRINT "Taste nach links gedrueckt" ELSEIF LCASE(taste) = "d" or taste = "6" THEN PRINT "Taste nach rechts gedrueckt" ELSE PRINT "keine Steuerungstaste gedrueckt" END IF SLEEP
Wie Sie sehen, steckt in diesem Quellcode eine Menge Schreibarbeit — und damit verbunden ein erhöhtes Risiko an Tippfehlern.
10.3.1 Grundaufbau
Eine alternative Bedingungsstruktur bietet SELECT CASE
. Hier wird nur einmal die Bedingung angegeben; danach folgen nur noch die gewünschten Vergleichswerte. SELECT CASE
hat folgenden Grundaufbau:
SELECT CASE Ausdruck CASE Ausdrucksliste1 Anweisung1 CASE Ausdrucksliste2 Anweisung2 ' ... CASE ELSE Anweisung END SELECT
Mithilfe von SELECT CASE
kann man Quelltext 10.8 folgendermaßen umformulieren:
DIM AS STRING taste PRINT "Druecke eine Steuerungstaste." taste = INPUT(1) SELECT CASE LCASE(taste) CASE "w", "8" PRINT "Taste nach oben gedrueckt" CASE "s", "5" PRINT "Taste nach unten gedrueckt" CASE "a", "4" PRINT "Taste nach links gedrueckt" CASE "d", "6" PRINT "Taste nach rechts gedrueckt" CASE ELSE PRINT "keine Steuerungstaste gedrueckt" END SELECT SLEEP
Der Ablauf ist ähnlich wie bei IF
. Zuerst wird der Ausdruck ausgewertet und dann in die erste CASE
-Zeile gesprungen, auf welche der Ausdruck zutrifft. Alle Zeilen zwischen dieser und der nächsten CASE
-Zeile werden ausgeführt, danach fährt das Programm am Ende des SELECT
-Blockes fort. Trifft keine der Ausdruckslisten zu, werden die Zeilen hinter dem CASE ELSE
ausgeführt, sofern vorhanden.
In jeder Ausdrucksliste können ein oder auch mehrere Vergleichswerte stehen, die dann durch Komma voneinander getrennt werden. Diese Liste kann beliebig lang werden. Es gibt auch noch weitere Möglichkeiten zur Angabe der Vergleichswerte, auf die in Kapitel 10.3.2 eingegangen wird.
Da der auszuwertende Ausdruck — hier der in Kleinbuchstaben umgewandelte Wert der Tastatureingabe — nur einmal angegeben werden muss, wird das Programm deutlich übersichtlicher. Allerdings gibt es auch einen programmtechnischen Vorteil: Die Umwandlung in Kleinbuchstaben, die ja durchaus etwas Rechenzeit kostet, muss nur einmal erledigt werden statt viermal wie in Quelltext 10.8. Und sollte sich der auszuwertende Ausdruck einmal ändern, muss dies auch nur an einer Stelle geschehen anstatt in jeder ELSEIF
-Zeile. Dafür sind Sie aber auf einen einzigen Ausdruck für den gesamten Block eingeschränkt.
Zu beachten ist, dass immer nur einer der CASE
-Abschnitte ausgeführt wird, auch wenn der Ausdruck bei mehreren Abschnitten angegeben wird. In einem solchen Fall wird immer der erste passende Abschnitt gewählt.
Vergleich mit anderen Sprachen: Im Gegensatz zur entsprechenden C-Funktion switch wird der SELECT -Block nach Abarbeitung des passenden CASE -Abschnitts verlassen; die Verwendung einer break -Anweisung ist nicht nötig. Allerdings lassen sich auch in FreeBASIC die SELECT -Blöcke durch Kontrollanweisungen vorzeitig verlassen (siehe dazu Kapitel 11.5.2). Es ist dagegen nicht möglich, in den folgenden CASE -Abschnitten fortzufahren! |
10.3.2 Erweiterte Möglichkeiten
Neben der einfachen Auflistung aller passenden Werte gibt es für die Ausdrucksliste noch weitere Möglichkeiten:
-
Bereichsangaben:
Mitwert1 TO wert2
kann überprüft werden, ob der Ausdruck im Bereich vonwert1
biswert2
liegt. Er muss also größer oder gleichwert1
sowie kleiner oder gleichwert2
sein. Bereichsangaben sind auch bei Zeichenketten möglich, jedoch sind hier die Besonderheiten von String-Vergleichen zu beachten (siehe Kapitel 10.2.1). -
Vergleichsoperatoren:
Die üblichen Vergleichsoperatoren <, >, <= und >= können eingesetzt werden, indem vor den Vergleichsoperator das SchlüsselwortIS
gesetzt wird. -
Alle drei Varianten (Einzelwerte, Bereichsangaben, Vergleichsoperatoren) können beliebig kombiniert werden, indem man sie durch Kommata getrennt auflistet.
Dazu ein Beispiel aus der FreeBASIC-Referenz (dient nur zur Veranschaulichung und ist nicht ohne Änderung lauffähig):
SELECT CASE a ' ist a = 5? CASE 5 ' ist a ein Wert von 5 bis 10? ' Die kleinere Zahl muss zuerst angegeben werden CASE 5 TO 10 ' ist a groesser als 5? CASE IS > 5 ' ist a gleich 1 oder ein Wert von 3 bis 10? CASE 1, 3 TO 10 ' ist a gleich 1, 3, 5, 7 oder b + 8? CASE 1, 3, 5, 7, b + 8 END SELECT
10.3.3 SELECT CASE AS CONST
Unter bestimmten Bedingungen kann der Compiler angewiesen werden, den SELECT
-Block effizienter umzusetzen und dadurch die Ausführungsgeschwindigkeit zu erhöhen (was natürlich nur dann ins Auge fällt, wenn der Block während des Programmablaufs sehr oft ausgeführt werden muss).
-
Der Ausdruck muss eine Ganzzahl sein.
-
Die Ausdruckslisten dürfen nur Konstanten oder einfache Ausdrücke enthalten, also Ausdrücke, die keine Variablen enthalten. Die FreeBASIC-internen mathematischen Funktionen dürfen ebenfalls verwendet werden.
-
Der Operator
IS
steht nicht zur Verfügung. -
Die Differenz zwischen dem kleinsten und dem größten Vergleichswert kann maximal 4096 betragen. Möglich sind also z. B. Werte im Bereich von 0 bis 4096 oder von 4 bis 4100.
Die Syntax lautet hier
SELECT CASE AS CONST Ausdruck
Der Hintergrund für die Einschränkungen ist, dass bei diesem Aufruf die durch die Ausdruckslisten festgelegten Sprungmarken bereits beim Compilieren festgelegt werden. Deswegen dürfen die Ausdruckslisten nur Werte enthalten, die bereits beim Compilieren feststehen (also keine Variablen).
10.4 Bedingungsfunktion: IIF()
Als dritte Möglichkeit steht die Funktion IIF()
zur Verfügung. Diese funktioniert ein gutes Stück anders als die beiden vorher genannten Bedingungsblöcken: Je nachdem, ob die übergebene Bedingung wahr oder falsch ist, wird einer von zwei Werten zurückgegeben.
Rueckgabe = IIF(Bedingung, Rueckgabewert_Wenn_Wahr, Rueckgabewert_Wenn_Falsch)
Es ist eine Kurzform von
IF Bedingung THEN Rueckgabe = Rueckgabewert_Wenn_Wahr ELSE Rueckgabe = Rueckgabewert_Wenn_Falsch END IF
Die beiden möglichen Rückgabewerte müssen denselben Datentyp besitzen und zum Datentyp der Variablen Rueckgabe passen. Die Stärke von IIF()
besteht darin, dass es auch in Ausdrücken eingebaut werden kann, z. B. in eine Berechnung oder einer Textausgabe. Dazu ein paar Beispiele:
' Geld um 100 reduzieren, aber nicht unter 0 geld = IIF(geld > 100, geld-100, 0) ' Gehaltstruktur abhaengig von den Dienstjahren gehalt = 1000 + IIF(dienstjahre > 10, dienstjahre*50, dienstjahre*20) ' Rueckmeldung: Einlass erst ab 18 PRINT "Du darfst hier " & IIF(alter>=18, "selbstverstaendlich", "nicht") & " rein!"
10.5 Welche Möglichkeit ist die beste?
Wenn eine feststehende Variable oder ein Rechenausdruck auf mehrere verschiedene Ergebnisse hin geprüft werden soll, ist meistens SELECT CASE
die beste Wahl. Der Quelltext ist damit am einfachsten zu lesen und zu warten. Allerdings kann SELECT CASE
nur einen einzigen Ausdruck abprüfen; auf den Inhalt zweier oder mehr verschiedener Variablen kann nicht gleichzeitig geprüft werden. In diesem Fall bleibt nur der Einsatz von IF
(verschachtelte SELECT
-Blöcke sind natürlich möglich, und ob sie sinnvoll sind, muss im Einzelfall geprüft werden). Die Stärke von IF
besteht in der hohen Flexibilität.
Als Beispiel für einen Einsatz von SELECT CASE
: Sie wollen ein tastaturgesteuertes Menü programmieren. Am Bildschirm werden fünf verschiedene Menüeinträge angezeigt, jeder mit einer Angabe versehen, mit welcher Taste er aufgerufen werden kann. In diesem Fall ist SELECT CASE
ideal geeignet. Als weiteres Beispiel kann eine „Übersetzung“ von Schulnoten in ihre Textbedeutung dienen (was sich jedoch leichter über ein Array regeln lässt).
Dagegen lässt sich allein schon bei einer Abfrage von Benutzernamen und Passwort ein SELECT CASE
nicht sinnvoll einsetzen. Sowohl IF
als auch SELECT CASE
haben also, je nach konkreten Fall, ihre Stärken.
IIF()
wiederum lässt sich (ausschließlich) dann gewinnbringend einsetzen, wenn ein Rückgabewert in Abhängigkeit von der Bedingung gefordert ist. Ein IIF()
lässt sich immer auch durch einen IF
-Block ersetzen, benötigt dann aber oft mehr Schreib- und Speicheraufwand, da der Rückgabewert in der Regel zusätzlich in einer Variablen zwischengespeichert werden muss.
10.6 Fragen zum Kapitel
-
Welchen Sinn haben Einrückungen im Quelltext?
-
Welche Werte werden in FreeBASIC als wahr und welche als falsch interpretiert?
-
Nach welchem System werden Zeichenketten verglichen?
-
Was ist der Unterschied zwischen einem logischen Operator und einem Bit-Operator?
-
Wie muss eine Bedingung formuliert werden, wenn der Wert der Variablen
a
zwischen 3 und 8 oder aber zwischen 12 und 20 liegen soll? -
Wieder eine kleine Programmieraufgabe: Das Programm soll den Benutzer nach Namen, Passwort und Alter fragen. Wenn der Name und das Passwort richtig sind, wird je nach Alter (verschiedene Altersbereiche wie „höchstens 14“, „zwischen 14 und 18“ und „mindestens 18“) eine andere Meldung ausgegeben.
Das Programm soll dann dahingehend erweitert werden, dass es nicht nur einen Benutzernamen mit zugehörigem Passwort erkennt, sondern zwei verschiedene Benutzernamen, jedes mit einem eigenen Passwort.