Entscheiden

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).

Flags 'erzeugen'

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:

< ( n1 n2 -- f )

und

< ( n1 n2 -- f )

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.

Boolsche Logik

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.

Mit Flags 'rechnen'

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 AND zwei (oder mehr) Bedingungen zu einer (boolschen) Und-Verknüpfung vereinigen lassen, so geht das Gleiche mit + zu einer Oder-Verknüpfung, man kann allerdings auch OR verwenden.

Eingebaute IF-Worte

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.

Aufgaben

  1. Schreiben sie das Wort ZIGARETTEN, das eine Altersangabe auf dem Stack erwartet und entsprechend dieser Angabe ausgibt, ob Zigaretten verkauft werden dürfen oder nicht. [Lösung]
  2. Schreiben sie ein Wort VORZEICHEN, das eine Zahl auf dem Stack erwartet und ausgibt, ob diese Zahl positiv, negativ oder Null war. [Lösung]
  3. In diesem Kapitel wurde ein Mehrfachtest vorgestellt, mit dem verschiedene Werte verschiedenen Zuständen zugeordnet werden konnten. Hier geht es nun darum zu testen, ob ein Wert innerhalb gegebener Grenzen liegt. Das Wort soll so aufgerufen werden:

    : GRENZE ( wert untere-grenze obere-grenze -- f ) ... ;

    [Lösung]