Die while-Schleife und die repeat-Schleife in R

Die while-Schleife und die repeat-Schleife sind etwas allgemeiner als die for-Schleife. Wenn man weiß, wie oft eine Schleife durchlaufen werden soll, ist die for-Schleife weniger fehleranfällig einzusetzen. Ist dagegen nur die Bedingung bekannt, unter die Schleife verlassen werden soll, muss man die while- oder repeat-Schleife einsetzen. Die while-Schleife realisiert die kopfgesteuerte Schleife, die repeat-Schleife besitzt keine Bedingungsprüfung. Wie bei der for-Schleife können die Schlüsselwörter break (vorzeitiges Verlassen der Schleife) und next (sofortiger Übergang zum nächsten Schleifen-Durchlauf) eingesetzt werden.

Einordnung des Artikels

Viele der Bezeichnungen aus Die for-Schleife in R werden hier ebenfalls verwendet, ohne sie erneut einzuführen. Für Leser, die zum ersten Mal mit Schleifen konfrontiert werden, wird daher empfohlen, vor diesem Kapitel Die for-Schleife in R durchzuarbeiten.

Einführung

Arten von Schleifen

In der Einführung zu Die for-Schleife in R wurden die drei Arten von Schleifen kurz vorgestellt:

Die Schleife mit Zählvariable (for-Schleife) wurde dort ausführlich besprochen; die kopfgesteuerte und die fußgesteuerte Schleife werden in diesem Kapitel erläutert.

Die Unterschiede dieser Schleifen können jetzt schon benannt werden:

  1. Bei der Schleife mit Zählvariable muss schon vor Betreten der Schleife bekannt sein, wie viele Durchläufe stattfinden werden, da in der for-Anweisung der Vektor angegeben wird, über den iteriert wird. (Streng genommen ist diese Aussage nicht ganz richtig, da man mit Hilfe von break die Schleife vorzeitig verlassen kann.)
  2. Die kopfgesteuerte und die fußgesteuerte Schleife sind allgemeiner als die Schleife mit Zählvariable, da sie durch eine Bedingungsprüfung gesteuert werden: Ist die Bedingung erfüllt, wird die Schleife nochmals ausgeführt. Im Allgemeinen kann man daher nicht immer voraussagen, wie oft die Schleife durchlaufen wird. Sie kann sogar unendlich oft durchlaufen werden; man spricht dann von einer nicht terminierenden Schleife.
  3. Umgekehrt kann aber jede for-Schleife in eine kopfgesteuerte oder fußgesteuerte Schleife umformuliert werden. Oft ist es eine reine Geschmacksfrage, welche Art der Schleife gewählt wird.

Die kopfgesteuerte und die fußgesteuerte Schleife im Pseudocode

Wie die kopfgesteuerte und die fußgesteuerte Schleife durch eine Bedingungsprüfung vor beziehungsweise nach dem Schleifenkörper gesteuert wird, kann wieder am Paradebeispiel "Personalien aufnehmen" demonstriert werden (siehe Die for-Schleife in R und Abbildung 1, 2):

Abbildung 1: Die kopfgesteuerte Schleife zum Aufnehmen der Personalien von N Personen. Ob die Schleife durchlaufen werden muss oder ob sie übersprungen wird, entscheidet sich vor dem Durchlauf der Schleife. Bei jedem Durchlauf verringert sich die Anzahl der Personen, die noch abgearbeitet werden müssen, daher die Anweisung N=N - 1.Abbildung 1: Die kopfgesteuerte Schleife zum Aufnehmen der Personalien von N Personen. Ob die Schleife durchlaufen werden muss oder ob sie übersprungen wird, entscheidet sich vor dem Durchlauf der Schleife. Bei jedem Durchlauf verringert sich die Anzahl der Personen, die noch abgearbeitet werden müssen, daher die Anweisung N = N - 1.

Abbildung 2: Die fußgesteuerte Schleife zum Aufnehmen der Personalien von N Personen. Im Unterschied zur kopfgesteuerten Schleife werden jetzt zuerst die Personalien einer Person aufgenommen und dann erst wird abgefragt, ob die Schleife nochmals durchlaufen werden muss. Wie oben verringert sich bei jedem Durchlauf die Anzahl der Personen, die noch abgearbeitet werden müssen, daher wieder die Anweisung N=N - 1.Abbildung 2: Die fußgesteuerte Schleife zum Aufnehmen der Personalien von N Personen. Im Unterschied zur kopfgesteuerten Schleife werden jetzt zuerst die Personalien einer Person aufgenommen und dann erst wird abgefragt, ob die Schleife nochmals durchlaufen werden muss. Wie oben verringert sich bei jedem Durchlauf die Anzahl der Personen, die noch abgearbeitet werden müssen, daher wieder die Anweisung N = N - 1.

Im Pseudocode – jetzt allgemein und nicht mehr für das spezielle Beispiel – würde man die Schleifen aus den Abbildungen 1 und 2 wie folgt realisieren:

# kopfgesteuerte Schleife:

solange (Bedingung)
    wiederhole (Anweisung)
# fußgesteuerte Schleife:

wiederhole (Anweisung)
    bis (Bedingung)

Realisierung in R

Die Realisierung der kopfgesteuerten und fußgesteuerten Schleife geschieht in R mit folgenden Konstrukten:

# kopfgesteuerte Schleife:

while(cond) expr

# fußgesteuerte Schleife:

repeat expr

Man beachte, dass die kopfgesteuerte Schleife genau den Pseudocode umsetzt, dagegen unterscheidet sich die fußgesteuerte Schleife vom Pseudocode:

  1. Bei der kopfgesteuerten Schleife wird zunächst eine Bedingung cond geprüft; ist sie erfüllt, wird der Anweisungs-Block expr ausgeführt. Anschließend wird erneut die Bedingung geprüft und so weiter. Erst wenn die Bedingung cond gleich FALSE ist, wird die Schleife verlassen.
  2. Bei der fußgesteuerten Schleife wird sofort der Anweisungs-Block expr ausgeführt. Ist er abgearbeitet, wird er erneut ausgeführt und so weiter. Im Gegensatz zur Formulierung im Pseudocode besitzt die repeat-Schleife keine Bedingungsprüfung und wird daher im Normalfall als Endlos-Schleife ausgeführt. Um sie zum Anhalten zu bringen, muss man eine break-Anweisung einfügen.

Die folgenden Abschnitte geben einige Beispiele für die while- und die repeat-Schleife.

Die while-Schleife

Die while-Schleife als Ersatz für eine for-Schleife

Im Kapitel über die for-Schleife wurde mehrmals das Beispiel angeführt, wie die Zahlen von 1 bis 100 addiert werden. Dieses Problem lässt sich auch mit einer while-Schleife lösen, im nächsten Unterabschnitt wird aber gezeigt, dass dies nicht der typische Einsatz von while ist.

Das Beispiel aus Die for-Schleife in R zum Addieren der Zahlen lautete:

idx <- (1:100)
summe <- 0

for(i in idx){
  summe <- summe + i
}

cat("Summe: ", summe, "\n")
# Summe:  5050

Eine gleichwertige Realisierung mit einer while-Schleife könnte wie folgt aussehen:

i <- 1
summe <- 0

while(i < 101){
    summe <- summe + i
    i <- i + 1
}

cat("Summe: ", summe, "\n")
# Summe:  5050

Zeile 1 und 2: Es werden die beiden Variablen i (Zählvariable) und summe vorbereitet; in summe werden die Zwischensummen abgespeichert, am Ende enthält summe die Summe der Zahlen von 1 bis 100.

Zeile 4 bis 7: Die while-Schleife mit der Bedingungsprüfung in den runden Klammern und dem Schleifenkörper in den geschweiften Klammern.

Im Schleifenkörper wird die Summe gebildet, indem zum aktuellen Wert der Zwischensumme der aktuelle Wert von i addiert wird (Zeile 5). Und die Zählvariable i wird um 1 erhöht (Zeile 6).

Ist die Bedingung in den runden Klammern (Zeile 4) gleich TRUE, werden die Anweisungen des Schleifenkörpers ausgeführt. Möchte man daher bis i = 100 summieren, muss die Bedingung lauten i < 101 ; denn sobald i == 101 erreicht ist, wird der Schleifenkörper nicht mehr ausgeführt, die Schleife verlassen und es erfolgt die cat()-Anweisung aus Zeile 9.

Vergleicht man die for-Schleife mit der entsprechenden while-Schleife, sollte sofort klar sein, dass die Konfiguration der for-Schleife weniger fehleranfällig ist: bei der while-Schleife muss man sich sowohl bei der Initialisierung als auch bei der Beendigung der Schleife Gedanken machen, ob die Variablen die richtigen Werte besitzen; bei der for-Schleife sind die Werte der Variablen leichter nachvollziehbar.

Der typische Einsatz einer while-Schleife

Das Beispiel oben sollte nur zeigen, dass eine for-Schleife immer durch eine while-Schleife ersetzt werden kann. Es beschreibt nicht den typischen Anwendungsfall einer while-Schleife. Denn oftmals weiß man nicht im Voraus, wie oft eine Schleife durchlaufen werden soll, sondern man kennt nur die Bedingung, wann sie verlassen werden soll. Dies ist etwa der Fall, wenn ein Nutzer eines Programmes immer neue Zahlen eingibt, die verarbeitet werden sollen und die Eingabe zu einem Ende kommt, wenn anstelle einer Zahl ein vereinbartes Zeichen eingegeben wird. Jetzt kann man den Abbruch der Schleife nur über die Bedingung formulieren und nicht durch einen vorgegebenen Vektor, den man schon vor den Nutzer-Eingaben setzen müsste.

Ein anderes typisches Beispiel für eine while-Schleife zeigt die folgende Fragestellung: Es sollen solange die Zahlen 1, 2, 3, ... addiert werden, bis ihre Summe eine gewisse Grenze überschreitet. Ohne die Formel für die Summe

1 + 2 + 3 + ... + n = n · (n+1)/2

kann man nicht voraussagen, wie viele Summanden es gibt, bis die Grenze erreicht wird. Aber das Problem kann mit Hilfe einer while-Schleife erledigt werden – ohne die entsprechende Formel zu verwenden. Das folgende Skript zeigt eine mögliche Realisierung mit der Grenze 1000:

i <- 1
summe <- 0

while(summe < 1000){
    summe <- summe + i
    i <- i + 1
}

cat("Summe: ", summe, "\n")
# Summe:  1035
cat("Index: ", i, "\n")
# Index:  46

# Kontrolle
sum(1:45)
# [1] 1035

Das Skript zeigt aber auch, dass es im Vergleich zur for-Schleife schwieriger ist nachzuvollziehen, welche Werte die Variablen beim Verlassen der Schleife haben. Hier können sich leicht Fehler einschleichen und daher soll das Skript ausführlich beschrieben werden – insbesondere welche Befehle beim letzten Durchlauf der Schleife noch abgearbeitet werden. Dem Programmier-Anfänger wird dringend empfohlen, dies immer im Trockentest nachzuprüfen.

Zeile 1 und 2: Wie oben werden die beiden Variablen i (Zählvariable) und summe vorbereitet.

Zeile 4 bis 7: Im Schleifenkörper der while-Schleife wird:

Der Schleifen-Durchlauf wird wiederholt, solange summe kleiner ist als 1000 (Zeile 4).

An der Kontroll-Ausgabe (Zeile 15 und 16) erkennt man, dass

1 + 2 + 3 + ... + 45 = 1035.

Mit diesem Wissen ist es leichter nachzuvollziehen, wie die Schleife verlassen wird:

Nachdem die Schleife mit i = 44 durchlaufen wurde, ist summe = 990 und i wird auf 45 erhöht. Hier sieht man, wie wichtig die Reihenfolge der Anweisungen im Schleifenkörper ist: Bei einer Vertauschung von Zeile 5 und 6 ergibt sich ein anderes Verhalten.

Da summe < 1000 erfüllt ist, wird die Schleife nochmals durchlaufen: Jetzt wird summe = 990 + 45 berechnet, was 1035 ergibt. Und i wird auf 46 erhöht.

Da jetzt die Bedingung summe < 1000 gleich FALSE ist, wird der Schleifenkörper nicht mehr aufgerufen und die Schleife verlassen. Die Variablen i und summe besitzen somit die Werte i = 46 und summe = 1035.

Gefragt war aber nach der Summe der Zahlen von 1 bis n, so dass die Summe noch kleiner ist als 1000. Dafür muss man wieder zu i = 44 und summe = 990 aus dem vorletzten Schleifen-Durchlauf zurückgehen:

1 + 2 + 3 + ... + 44 = 990.

Die folgende Aufgabe soll verdeutlichen, wie wichtig es ist, die Werte der Variablen beim Betreten und Verlassen der while-Schleife zu kennen.

Aufgabe: Führen sie diesen Trockentest durch, wenn im Skript oben die Zeilen 5 und 6 vertauscht werden:

  1. Welche Werte haben i und summe beim ersten und zweiten Durchlauf der Schleife?
  2. Welche Werte haben i und summe nach dem Verlassen der Schleife?

Die Schlüsselwörter break und next

Im Kapitel Die for-Schleife in R wurde im Abschnitt Die Schlüsselwörter break und next erklärt, wie man break und next zur zusätzlichen Steuerung der Schleife einsetzen kann:

  1. Die Anweisung break sorgt dafür, dass die Schleife vorzeitig verlassen wird.
  2. Die Anweisung next sorgt dafür, dass der folgende Teil des Schleifenkörpers übersprungen wird und der nächste Schleifen-Durchlauf startet.

Diese beiden Schlüsselwörter haben in der while-Schleife dieselbe Bedeutung wie in der for-Schleife und werden nicht nochmals erläutert.

Die repeat-Schleife

In der Einführung wurde schon darauf hingewiesen, dass die repeat-Schleife nicht realisiert, was man im Pseudocode als fußgesteuerte Schleife bezeichnet; auch in vielen anderen Programmiersprachen ist die repeat-Schleife als fußgesteuerte Schleife realisiert. Dagegen hat sie in R keine Bedingungsprüfung und führt daher eigentlich zu einer Endlos-Schleife.

Beim Einsatz der repeat-Schleife ist daher vom Programmierer darauf zu achten, dass keine Endlos-Schleife entsteht. Dazu wird eine Bedingungsprüfung eingebaut, die eine break-Anweisung enthält. Das folgende Skript zeigt ein Beispiel:

n <- 2

repeat {
  n <- n^2
  if(!is.finite(n)) break
  cat(n, "\n")
}

# 4 
# 16 
# 256 
# 65536 
# 4294967296 
# 1.844674e+19 
# 3.402824e+38 
# 1.157921e+77 
# 1.340781e+154

Die repeat-Schleife ist in den Zeilen 3 bis 7 enthalten.

Lässt man Zeile 5 weg, wird die Zahl n (ausgehend von n = 2) in jedem Schleifen-Durchlauf quadriert; sobald der Zahlenbereich für double überschritten wird, wird für n der "Wert" Inf ausgegeben.

In Zeile 5 wird dafür gesorgt, dass die Schleife verlassen wird, wenn n den Zahlenbereich überschreitet.

Das folgende Skript zeigt die Berechnung der Summe der natürlichen Zahlen 1 + 2 + ... bis zur Schranke 1000 für die Summe – jetzt realisiert mit einer repeat-Schleife:

i <- 1
summe <- 0

repeat{
  summe <- summe + i
  i <- i + 1
  if(summe > 1000) break
}
 
cat("Summe: ", summe, "\n")
# Summe:  1035
cat("Index: ", i, "\n")
# Index:  46

Aufgabe:

Versuchen Sie wieder im Trockentest nachzuvollziehen, welche Werte i und summe haben, wenn die Schleife verlassen wird.

Zusammenfassung

while-Schleife

while(cond) expr

Solange die Bedingung cond gleich TRUE ist, wird der Anweisungs-Block expr ausgeführt. Ist cond gleich FALSE, wird die Schleife verlassen.

Zusätzlich kann die Schleife mit Hilfe von break und next gesteuert werden.

repeat-Schleife

repeat expr

Der Anweisungs-Block expr wird beliebig oft ausgeführt. Zur Steuerung muss zusätzlich eine break-Anweisung verwendet werden.

Wie bei der for-Schleife und bei der while-Schleife ist zusätzlich der Einsatz der next-Anweisung möglich.

Weitere Informationen über while-Schleifen und repeat-Schleifen findet man in der Dokumentation unter ?Control im Paket base.