[Allegro] Trick 60: Wiederholungstaten (Alles ueber Schleifen in FLEX)

Bernhard Eversberg ev at biblio.tu-bs.de
Mo Nov 12 08:42:19 CET 2007


Trick 60:  Wiederholungstaten
            Alles Wichtige über Schleifen in FLEX

Aufgabe: Einfachstmöglich eine Befehlsfolge immer wieder abarbeiten,
          bis ein bestimmter Wert oder eine Bedingung auftritt.

Warum: Das zyklische Durchlaufen von Befehlsfolgen gehört zu den
        häufigsten Übungen beim Programmieren. Dafür muß es einfache
        Rezepte geben, die man routinemäßig anwenden kann.
        So etwas nennt man auch "Schleife", weil dabei wieder und
        wieder zum Anfangspunkt einer Befehlsfolge zurückgekehrt wird.

Lösung:
Zuerst etwas Theorie
Richtige Programmiersprachen (so eine ist FLEX nicht) haben drei
oder vier Lösungen. Schematisch sehen sie folgendermaßen aus:

1. Die for-Schleife      for(anfangswert; bedingung; schrittbefehl)
    ----------------              <befehle>

    Das ist wohl die beliebteste Form.
    Der "for"-Befehl setzt einen Anfangswert, eine Bedingung
    und einen Schrittbefehl. Die dann folgenden <befehle> werden so
    oft ausgeführt, und jeweils danach der Schrittbefehl, wie die
    bedingung noch gilt.
    Falls die bedingung gleich schon zu Anfang nicht gilt, werden
    die  <befehle>  gar nicht ausgeführt.

2. Die while-Schleife    while(bedingung)
    ------------------            <befehle>

    <befehle> werden so lange immer wieder ausgeführt, wie bedingung
    gilt. Gilt die bedingung schon am Anfang nicht, passiert nichts.

3. Die do-Schleife       do
    ---------------               <befehle>
                          while(bedingung)

    <befehle> werden ausgeführt, dann bedingung geprüft, und wenn
    erfüllt, abermals <befehle>, bis bedingung nicht mehr gilt.
    Die Befehle werden also mindestens einmal ausgeführt, im
    Gegensatz zu 2.

4. Die goto-Schleife     wenn nicht bedingung dann goto weiter
    -----------------     :marke
                                  <befehle>
                          wenn bedingung dann goto marke
                          :weiter

    Die erste Zeile läßt man weg, wenn die <befehle> mindestens einmal
    ausgeführt werden sollen.
    Diese Form wird von Informatikern gering geschätzt, weil sie
    wenig übersichtlich ist und deshalb auch fehlerträchtig, vor allem
    ist sie kaum prüfbar, z.B. von einem Compiler oder Interpreter.
    Es gab Bestrebungen, sie in Acht und Bann zu tun, dazu ist es aber
    nicht gekommen.

Wichtig zu wissen: Alle 4 Formen sind logisch gleichwertig. Was man
mit einer davon machen kann, kann man auch mit jeder der anderen drei
Formen machen, allenfalls mit einer zusätzlichen Bedingungsprüfung.
Programmierer können sich folglich entscheiden, auf eine oder
mehr als eine davon ganz zu verzichten. Ob eine Konstruktion
übersichtlich und verständlich ist, hängt aber nicht nur von
ihrem Typ ab, sondern auch von der Art, wie sie umgesetzt ist,
wozu auch Kommentare gehören. So *kann* selbst der Typ 4 durchaus
übersichtlich und verständlich realisiert werden, der Typ 1 dagegen
erzwingt geradezu ein hohes Maß an Übersichtlich- und Durchschau-
barkeit, aber die Formulierung der Bedingung kann manchmal recht
abstrakt und schwer durchschaubar sein - Gewohnheitssache.
Form 1 ist scheinbar suboptimal in Fällen, wo die Anzahl der
Schleifendurchläufe nicht von einem Zähler abhängt. Mit geschickter
Wahl der Bedingung kann man das aber leicht umgehen und dann den
Schrittbefehl und sogar den Anfangswert weglassen - damit hat man
dasselbe wie Form 2.
Alle vier Formen stehen in der Gefahr, daß bei ungenügend durch-
dachten Bedingungen Endlosschleifen entstehen können. Es ist sogar
grundsätzlich unmöglich, dies softwaretechnisch auszuschließen.

FLEX kennt leider nur den Typ 4, wobei es scheinheiligerweise nicht
goto heißt, sondern jump. Man muß also überlegen, wie man damit
geschickt und sicherheitsbewußt umgeht, aber schwer ist das nicht.
Wir zeigen es an zwei Beispielen, die man als Schemata leicht
übernehmen und verinnerlichen kann.

A. Schleife mit Zähler
======================
Für die Zahlen von 1 bis 100 sollen bestimmte Dinge ausgeführt
werden, die hier symbolisch durch <befehle> dargestellt sind:

    z=1
    :loop
    <befehle>
    z+1
    if z<101 jump loop

Innerhalb der <befehle> kann z auch noch vorkommen, muß aber nicht.
Aufzupassen ist, z nicht innerhalb der <befehle> zu *verändern*, es
sei denn, aus genau überlegten Gründen. Beim if-Befehl wird der
momentane Wert von z genommen, egal wie er zustandekam.

Hinweis: Als Name für eine Schleifenvariable nehmen Programmierer
gewohnheitsmäßig oft das i. In FLEX gibt es das nicht, da ist z
die einzige ganzzahlige Variable und Z die einzige Gleitkommavariable.
Mit den sonstigen Variablen kann man jedoch auch gut rechnen, nur
nicht ganz so direkt wie mit z und Z.
Ersatzweise kann man z.B. so arbeiten - mit $i statt i:

    $i=1
    :loop
    <befehle>
    eval $i+1
    ins $i
    if <101 jump loop
      --- Ende :loop ---

Hier steht vor dem <101 kein $i! Es wird vielmehr der Inhalt der
internen Variablen genommen, und das ist in dem Moment eben noch
der Wert von $i. Verbal kann man die letzten drei Zeilen so ausdrücken:
Addiere 1 auf den Wert von $i, setze das Ergebnis in die Variable $i,
vergleiche diesen Wert mit der Zahl 101, und wenn er kleiner ist,
springe zur Marke :loop.
Variante: Die Endzahl kann auch in einer #u-Variablen stehen oder in
einem Datenfeld. Dann sieht die Endprüfung so aus:

if <#nnn ..., also z.B.  if <#uxy jump loop   oder   if <#123 jump loop

Empfehlung: Eine Sprungmarke, die als Schleifenbeginn dient, benennt
man entweder :loop  oder mit einem Namen, in dem :loop vorkommt.
So wird visuell immer sofort klar, was da vorliegt. Einen Anfangswert,
wenn einer gebraucht wird, immer in der direkt vorangehenden Zeile
setzen. Zur visuellen Verdeutlichung *kann* man einen Kommentar, wie
hier  --- Ende :loop ---  unter das Schleifenende setzen.
Hinweis: Wenn zwei gleiche Sprungmarken in einem FLEX vorkommen, dann
          gilt nur die erste.

B. Schleife mit beliebiger logischer Bedingung
==============================================
    In FLEX sehr viel häufiger als Zählschleifen! Aber genaugenommen
    ist A. sowieso nur ein Spezialfall von B.
    Beispiel: Ergebnismenge abarbeiten
    Die Sätze einer Ergebnismenge sollen abgearbeitet werden, d.h. für
    jeden Satz sind bestimmte Befehle auszuführen. Dabei wird kein
    Zähler gebraucht. Hier das Muster:

first
:eloop
<befehle>
next
if yes jump eloop

    Im Hilfetext zu "next" (h xnext  eingeben) findet man auch das
    Muster, mit dem man die gesamte Datenbank abarbeiten kann.

Und wo ist jetzt der Trick? Ob man wirklich hier von Tricks sprechen
will, sei dahingestellt. Es sind zwei Dinge zu nennen, die dem
Einsteiger wie auch dem in anderen Sprachen bewanderten Programmierer
als Aha-Erlebnis erscheinen können:
1.
Eine FLEX-Schleife kann mehr als einen Rücksprung zum Schleifenanfang
haben. Das ist nicht nur praktisch, es ist hinsichtlich der Durchschau-
barkeit und Fehlerträchtigkeit auch mit Vorsicht zu genießen!
2.
Der Rücksprung zum Schleifenanfang kann nicht nur durch einen Zahlen-
vergleich ausgelöst werden, sondern durch jeden der zahlreichen if-
Befehle, die FLEX bereithält.

Zusatz-Trick: Ausbruch aus Endlosschleifen
Mit dem Einbau dieser zwei Zeilen in das Innere einer Schleife:

keycheck
if yes jump weiter

kann man erreichen, daß man sie mit Druck auf Esc abbrechen kann.
Wenn dann mal stundenlang einfach nichts zu geschehen scheint, entnervt
Esc drücken - und dann die Schleifenlogik nochmal durchdenken!





Mehr Informationen über die Mailingliste Allegro