dedlfix: Schleifen abbrechen

Beitrag lesen

Hi!

Aber wenn da noch weiterer Code hinzukommt, besonders welcher zwischen "found = true" und dem Schleifenende, braucht es noch ein continue.
Dem widerspreche ich einfach mal und behaupte: Es geht sicherlich auch ohne continue. Wenn du ein Beispiel lieferst, das ich nicht in eines ohne continue verwandeln kann, gebe ich mich geschlagen. ;-)

Ja gut, du kannst statt des continue mit if-else-Gebilden die Abarbeitung von Code verhindern. Das ergibt dann ungefähr so einen Code

*
    *
      *
        *

statt einem solchen mit Abbrüchen

*
  *
  *
  *

Das Ergebnis steht fest. Und es kommt auch kein Code, der noch irgendwas sinnvolles tun soll, denn sonst hätte die return-Variante nicht eingesetzt werden können. Warum also sollte man den nachfolgenden Code noch ausführen? Vielleicht macht oder veranlasst er sogar etwas, was gleich gar nicht gewünscht ist?
Diese abstrakte Diskussion fällt mir sehr schwer. Mir wäre lieber du brächtest ein dir möglichst fies erscheinendes Beispiel, über das wir reden können.

Ein einfaches Beispiel kommt gleich.

Ich behaupte weiter: In den allermeisten Fällen ist die Variante mit continue nicht essentiell eleganter. Guten Gegenbeispielen stehe ich aber aufgeschlossen gegenüber.
Eben, deswegen return und fertig.
Das verstehe ich wieder nicht: Ich sage ein continue a)nicht notwendig und b)obwohl möglich, auch nicht eleganter. Du sagst ein continue ist notwendig. Du merkst doch, dass wir uns widersprechen?

Ein continue ist nicht in jedem Fall notwendig. Es ist dann notwendig, wenn man den Rest des Schleifenkörpers nicht weiter durchlaufen möchte. Sei es, weil die Abarbeitung nur Zeit verbraucht, oder weil sie Änderungen vornimmt, die man nicht will.

Gegeben sei eine Datenstruktur. Zum Beispiel ein Array. Der Arrayzeiger steht auf einem der Elemente.

$found = false;  
while ($found) {  
  if (current($array) == ...) {  
    $found = true;  
    continue;  
  }  
  next($array);  
}  
unset($found);

Ergebnis ist, dass der Arrayzeiger nun auf dem gesuchten Element steht. Ohne continue stünde er auf dem nächsten Element. Das continue verhindert das unerwünschte Weiterstellen des Arrayzeigers.

Nicht nur, dass nun eine Variable namens $found rumliegt, die man mit einem weiteren Schritt aufräumen müsste/kann, auch sprachlich ist es verwirrend. Wenn das Element gefunden ist, soll die Suche abgebrochen werden und nicht fortgesetzt. Da du aber eine Schleife mit $found-Variable haben willst, muss sie fortgesetzt werden.

do {  
  if (!(current($array) == ...))  
    next($array);  
} while (current($array) == ...);

Mit einer fußgesteuerten Schleife wird es auch nicht besser, da der Zeiger nicht unbedingt weitergestellt werden soll. Wenn er bereits auf dem gesuchten Element steht, dann muss er dort bleiben. Wenn man die Bedingung nicht noch einmal auswerten möchte, braucht man wieder die Hilfsvariable:

$found = false;  
do {  
  if (current($array) == ...)  
    $found = true;  
  else  
    next($array);  
} while ($found);  
unset($found);

Eine Alternative zum Preis einer komplexeren Bedingung wäre:

$found = false;  
do {  
  if (!($found = current($array) == ...))  
    next($array);  
} while ($found);  
unset($found);

Meine Variante mit break

while (true) {  
  if (current($array) == ...)  
    break;  
  next($array);  
}

hat zwar den Makel des true als Schleifenbedingung, aber da es (außer goto) keine Schleifen ohne Bedingung gibt, kommt man daran nicht vorbei.

Dies war jetzt nur ein ganz kleines Beispiel. Nun stell dir eins vor, bei dem noch mehr Abbruchbedingungen getestet werden müssen.

while (true) {  
  $current = current($array);  
  
  if ($current == dies)  
    break;  
  
  if ($current == das)  
    break;  
  
  if ($current == jenes)  
    break;  
  
  next($array);  
}

So wie ich das eben notiert habe, kann man sicher dies, das und jenes in einem if testen. Es muss aber einzeln bleiben, wenn vor dem jeweiligen break noch jeweils unterschiedlicher Code ausgeführt werden soll.

Das ist erst dann ein Fakt, wenn ich mich davon überzeugt habe. Dazu muss ich den gesamten Code kennen und brauch nicht nur das break zu sehen. Natürlich muss ich auch den gesamten Code sehen, um mir ein Bild von der Funktionalität zu machen.
Vielleicht liegt der Unterschied einfach darin, dass ich eher an einen Top-Down-Approach denke: Wenn ich eine Schleife lesen will, dann fange ich als erstes mit dem Kopf an und finde es da enorm hilfreich, wenn die dort angegebene Abbruchbedinung wirklich zutrifft. Natürlich muss ich den gesamten Schleifenrumpf auch kennen, um zu wissen, was da genau passiert.

Ja, aber das was du da im Kopf siehst, ist nur das Auswerten einer Hilfsvariable und nicht die eigentliche Abbruchbedingung. Diese steckt weiterhin im Schleifenkörper. Wenn sie wirklich im Kopf steckte, braucht es dafür weder return/break noch Hilfsvariablen/continue.

Dem liegt die Annahme zu Grunde, dass eine _vernünftige_ Schleife (1:1-Übertragungen mathematischer Algorithmen vielleicht ausgeschlossen) sowieso nie so lang sein wird, dass man sie nicht als ganzes erfassen kann.

Naja, du kannst eine komplexe Aufgabenstellung nicht weniger komplex machen, indem du sie aufteilst. Wenn du Glück hast, wird sie durch das Aufteilen übersichtlicher, dafür erhöht sich aber die Komplexität.

Es ist im Grunde wie mit Hundekot. Wenn da ein großer Haufen liegt, ist das zwar ärgerlich, aber wenn du ihn in Kotabschnitte auf- und über den Fußweg verteilst, kann man diese zwar einzeln betrachten, benötigt aber auch gezielte Sprünge und Schritte um alle Teile (zwecks Beseitung) erfassen zu können. Ganz abgesehen von den unschönen Nebenwirkungen beim unbeabsichtigten Fortführen des Kots ...

Du gehst vielleicht eher von einer unübersichtlich langen Schleife aus, von der du _nur einen Teil_ betrachten willst. Für mich wäre da schon wieder Kern des Problems, dass die Schleife zu lang ist. Wie auch bei unserer Diskussion über die sinnvolle Länge von Funktionen.

Um Code zu erfassen, muss ich das sicherlich Stück für Stück machen. Ich bin mir nur nicht sicher, ob es generell besser sein soll, diese Teile zu separieren oder sie an einem Ort zu lassen. Ich denke, für diesen Punkt der Diskussion gibt es keine allgemeingültige Lösung.

Nunja, mit Fehlern sollte man immer rechnen. Davor ist die Abbruchvariablen-Variante auch nicht gefeit. Rein rechnerisch steigt dabei die Fehlerwahrscheinlichkeit durch die zusätzliche Variable sogar noch.
Du hast aber nicht mit der Fehlerwahrscheinlichkeit argumentiert. Die halte ich durch die Einführung einer einzigen, sinnvoll benannten und verwendeten booleschen Variable nur für unwesentlich erhöht.

Diese eine Variable kommt an mindestens zwei Stellen vor. Eher an noch mehr, denn ansonten könnte man die eigentliche Bedingung vermutlich gleich im Schleifenkopf oder -fuß unterbringen. Jede Stelle ist einen weitere potentielle Fehlerstelle.

Lo!