Im Kapitel zur Bruchrechnung wurde schon mit der Entscheidung
IF
gearbeitet, so dass dieses Wort hier nicht mehr
vorgestellt werden muss. Was sie allerdings in diesem Kapitel lernen
werden, ist die genaue Funktionsweise dieses Wortes und wie man
damit arbeiten kann. Außerdem werden noch zwei Worte vorgestellt,
die ebenfalls Entscheidungen ermöglichen, allerdings eher in
bestimmten Situationen eingesetzt werden.
Noch mal zur Erinnerung. Das IF
-Wort gibt es in zwei
Varianten:
IF ... THEN
oder
IF ... ELSE ... THEN
In jedem Fall 'frisst' IF
die oberste Zahl vom Stack.
Ist diese Zahl nicht Null, dann wird der Teil
zwischen IF
und THEN
, beziehungsweise
zwischen IF
und ELSE
ausgeführt.
Ist die Zahl hingegen Null, dann wird in der ersten Variante einfach
hinter THEN
weiter gemacht. Gibt es das
ELSE
, dann wird, wenn die Zahl Null ist, dahinter
weiter gemacht und das THEN
einfach ignoriert. In einem
Überblick sieht das so aus:
Am einfachsten macht man sich das Ganze klar, indem man einfach mal zwei Worte definiert, die genau diese Entscheidungen nutzen. Das erste ist:
: IFTEST IF ." Hier der wahre Teil " THEN ." und hier der Rest" ;
Dieses Wort kann man mal mit ein paar Zahlen aufrufen und sich ansehen, was passiert:
1 IFTEST Hier der wahre Teil und hier der Rest ok 0 IFTEST und hier der Rest ok -1 IFTEST Hier der wahre Teil und hier der Rest ok
Interessant ist hier vor allem der dritte Versuch, an dem man sehen
kann, dass IF
sich wirklich nur dafür interessiert, ob
die Zahl Null ist oder nicht (Probieren sie das Wort mal ohne, dass
ein Wort auf dem Stack liegt). Eine solche Zahl nennt man in der
Sprache von Forth: Flag (Übrigens nicht nur in Forth. Die
Bezeichnung Flag findet sich in vielen Bereichen der Computerei. Das
Bild vermittelt das, was gemeint ist: Eine Flagge kann nur hoch oder
runter sein, ein Flag nur Null oder Nicht-Null).
Schreiben sie nun mal:
: ELSETEST ." Ich bin " IF ." wahr " ELSE ." falsch " THEN ." gewesen" CR ;
und spielen ein wenig mit diesem neuen Wort herum. Ich bin sicher, sie werden die Logik der 'Flags' schnell verstanden haben.
Bevor ich ihnen die Flags noch ein bisschen näher bringen will,
zuvor noch eine Bemerkung zur Schachtelung von IF
Wörtern. Es ist selbstverständlich möglich innerhalb einer
IF
-[ELSE]-THEN
Konstruktion
eine weitere unterzubringen, aber...
Das folgende Stück Source zeigt eine Variante, mit der man verschiedene Körpergrößen einer entsprechenden Aussage zuordnen kann, sehen sie selbst:
... DUP 100 < IF ." winzig " ELSE 150 < IF ." klein " ELSE 180 < IF ." normal " ELSE 200 < IF ." groß " ELSE ." riesig " THEN THEN THEN THEN ...
Wenn man sich die Ansammlung von THEN
am Ende ansieht,
kann man schnell auf die Idee kommen, dass das auch eleganter gehen
sollte (und verlassen sie sich drauf, es geht
eleganter).
Es gibt einige Wörter in Forth, die Flags 'erzeugen'. Das einfachste
ist =
:
= ( n1 n2 -- f )
Wie der Stackkommentar zeigt, erwartet =
zwei Zahlen
auf dem Stack. Sind diese beiden Zahlen gleich, dann wird 'wahr',
also eine Zahl, die nicht Null ist, normalerweise '-1', zurück
gegeben. Sind die Zahlen nicht gleich, wird eine Null, also 'falsch'
zurück gegeben. Ein einfaches Beispiel zeigt das:
: RATEZAHL 5 = IF ." gleich" ELSE ." ungleich" THEN CR ; 1 RATEZAHL ungleich ok 10 RATEZAHL ungleich ok 5 RATEZAHL gleich ok
Analog arbeiten die Wörter:
und
Die Notation entspricht dabei der bei den arithmetischen Wörtern. Somit sind die folgenden Ausdrücke identisch:
Infix | Postfix |
2 < 5 | 2 5 < |
13 > 12 | 13 12 > |
Dabei geben diese Wörter 'wahr' zurück, wenn die beiden Zahlen im mathematischen Sinne richtig beurteilt wurden. So ergibt
2 5 <
'wahr' und
13 2 <
'falsch'. Ebenfalls im mathematischen Sinne arbeiten die drei Wörter:
0< ( n1 -- f ), 0= ( n1 -- f ) und 0> ( n1 -- f)
die jeweils nur eine Zahl auf dem Stack erwarten und ein entsprechendes Flag zurück geben, wenn diese Zahl kleiner, gleich oder größer als Null ist.
Wieso betone ich so, dass diese Operatoren im mathematischen Sinne arbeiten? Nun, es gibt auch noch einen logischen. In der sogenannten boolschen Logik gibt es drei Operatoren: NOT, AND und OR. Diese Operatoren werden auf AUssagen angewendet, die 'wahr' oder 'falsch' sind, in diesem Sinne also den Flags in Forth entsprechen (In manchen Sichtweisen gibt es auch noch den Operator XOR, der aber mit den drei anderen dargestellt werden kann).
Der einfachste logische Operator ist 'NOT'. Er dreht einfach den Wahrheitswert einer Aussage um. -- Im folgenden werden Aussagen einfach mit 'A' und 'B' abgekürzt.
A | NOT A |
w | f |
f | w |
Der Operator OR wird auf zwei Aussagen angewendet. Der Wahrheitswert der OR-Operation ist nur dann falsch, wenn beide Ausgangsaussagen falsch waren, sonst ist er wahr. (Umgangssprachlich ist er wahr, wenn A oder B oder beide wahr waren).
A | B | A OR B |
w | w | w |
w | f | w |
f | w | w |
f | f | f |
Aus der AND-Operator wird auf zwei Aussagen angewendet, er ist allerdings nur dann wahr, wenn beide Aussagen wahr waren.
A | B | A AND B |
w | w | w |
w | f | f |
f | w | f |
f | f | f |
Soweit, so gut! In den meisten Forth-Systemen gibt es alle drei Worte, sie machen nur leider nicht das, was man von ihnen erwarten würde. Es sind zwar logische Operatoren, aber in dem Sinne, wie die boolsche Logik das vorsieht, arbeiten sie nur dann, wenn 'wahr' mit '-1' kodiert wird, bei allen anderen Werten klappt das leider nicht:
1 2 AND . 0 ok
Nach dem, was bisher hier im Kapitel stand, sollte man davon ausgehen, dass '1' und '2' beide für 'wahr' stehen. Wenn man nun zwei Wahrheitswerde mit 'AND' verknüpft, dann kommt in der boolschen Logik auch 'wahr' heraus. Das obige Ergebnis '0' wird in Forth aber als 'falsch' interpretiert. Was ist passiert?
Nun, in den Beschreibungen findet sich die Bemerkung, dass die Zahlen bitweise mit AND verknüpft wurden. Was soll das nun wieder heißen?
In einem vorherigen Kapitel habe ich beschrieben, dass der Speicher aus hintereinadner liegenden Zellen besteht, die ihrerseits aus Bits bestehen. Ohne jetzt hier auf die Details einzugehen (kommt noch) kann man sagen, dass eine Reihe von Bits in Computern als Zahlen interpretiert werden. Der Einfachheit gehe ich hier von 8 solcher Bits für eine Zahl aus (In Forth sind es 16 bis 64 Bits, aber das kommt später). Die Zahlen '1' und '2' werden in Bits kodiert folgendermaßen dargestellt:
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
1: | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
2: | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
Nun wird alles klar! Liest man eine Bit-'1' einer Zahl als 'wahr'
und eine Bit-'0' als 'falsch', dann stimmt die Sichtweise wieder.
Die AND
-Verknüpfung arbeitet einfach auf die einzelnen
Bits einer Zahl und nicht auf die Zahl als Ganzes. Auch wenn das
hier nicht Thema ist, kann ich jetzt schon mal darauf hinweisen,
dass diese Eigenschaft außerordentlich nützlich sein kann. Hier
braucht uns nur eines zu interessieren. Die '-1' wird in Bits
kodiert:
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
-1: | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
Bei der '-1' sind in der Bit-Darstellung alle Bits gleich '1' (Übrigens auch bei einer Länge von 16, 32 oder 64 Bits -- die Begründung kommt noch). Man kann sich nun leicht klar machen, wieso die drei Wörter in Forth dann wie erwartet arbeiten.
Wenn man die Hintergründe kennt und auf die Fallen bei den Worten
OR
, AND
und NOT
achtet, dann
sind in Forth einige Dinge recht einfach möglich. Vor allen, wenn es
um mehrfache Bedingungen geht. Stellen sie sich vor sie suchen ein
Stück Stoff, dass mindestens 90cm breit und mindestens 150cm lang
sein soll. In Forth prüfen sie das folgendermaßen:
: BREITGENUG ( breit lang -- flag ) 150 > SWAP 90 > AND IF ." Passt!" THEN ;
Versuchen sie die Definition mal mit ein paar Werten durch, sie werden sehen, das dieser eine Test wirklich reicht. Wenn eine oder beide Bedingungen nicht erfüllt sind, dann gibt das Wort auch nichts aus. Nur wenn beide Bedingungen erfüllt sind, 'sagt' es "Passt!" Das hängt damit zusammen, dass eine einzige 'Null' als Ergebnis, also ein 'falsch' keine anderen Werte mehr zulässt.
So wie wir gerade gesehen haben, dass sich mit +
zu einer
Oder-Verknüpfung, man kann allerdings auch OR
verwenden.
Stellen sie sich eine Situation vor, in der sie mit einer Zahl etwas machen wollen, wenn diese Zahl nicht Null ist (typisch ist eine Division). In so einem Fall müssten sie schreiben:
... DUP IF hier irgendwas ELSE DROP THEN ...
Der ELSE
-Teil ist nötig, da sie die Zahl, mit der sie
arbeiten wollten vor dem IF
duplizieren mussten, um ein
Flag zu haben. Allerdings enthält er nicht mehr als ein
DROP
, um die dann überflüssige Zahl vom Stack zu
entfernen.
Für eine derartige Situation hält Forth das Wort ?DUP
bereit. Es arbeitet wie DUP
, allerdings nur dann, wenn
die oberste Zahl auf dem Stack nicht Null ist. Sonst bleibt der
Stack unberührt. Das obige Problem lässt sich dann einfacher
schreiben als:
... ?DUP IF hier irgendwas THEN ...
Das Wort ?DUP
hat also ein IF
quasi mit
'eingebaut'. Ein weiteres Wort, bei dem das auch so ist, ist
ABORT"
. Auch ABORT"
kann nur innerhalb
einer Definition verwendet werden und dahinter wird eine mit einem
doppelten Anführungszeichen begrenzte Zeichenkette angegeben. Findet
ABORT"
ein wahres Flag auf dem Stack, dann bricht es
die weitere Ausführung ab, leert den Stack, gibt die Zeichenkette
und das Wort, innerhalb dessen es ausgeführt wurde aus. Mit diesem
Wort ist eine einfache Fehlerbehandlung möglich.
Stellen sie sich vor, sie schreiben ein Programm, bei dem Körpergrößen eine Rolle spielen. Sinnvollerweise können hier nur gewisse Werte auftreten. Eine Körpergröße von 1000cm ist zum Beispiel nicht sinnvoll. Wenn sie ihr Programm mit Ganzzahlen schreiben wollen, kann es gut sein, solche Situationen abzufangen. Der entsprechende Kode könnte so aussehen:
... DUP 1000 > ABORT" Unsinnige Größe!" ...
Ist die eingegebene Größe nicht größer als 1000, dann wird der Teil hinter der Zeichenkette "Unsinnige Größe!" ausgeführt, das Programm also einfach weiter geführt.
ZIGARETTEN
, das eine
Altersangabe auf dem Stack erwartet und entsprechend dieser Angabe
ausgibt, ob Zigaretten verkauft werden dürfen oder nicht. [Lösung]
: GRENZE ( wert untere-grenze obere-grenze -- f ) ... ;
[Lösung]