Soll ich die Unterbrechungsbedingung in einer for-Schleife nach Möglichkeit in das Bedingungsfeld verschieben? [geschlossen]

48

Manchmal brauche ich für Schleifen, die eine Pause wie folgt brauchen:

for(int i=0;i<array.length;i++){
    //some other code
    if(condition){
        break;
    }
}

Ich fühle mich beim Schreiben unwohl

if(condition){
    break;
}

weil es 3 Codezeilen verbraucht. Und ich fand die Schleife kann umgeschrieben werden als:

                                   ↓
for(int i=0;i<array.length && !condition;i++){
    //some other code
}

Meine Frage ist also, ob es eine gute Praxis ist, die Bedingung in das Bedingungsfeld zu verschieben, um Codezeilen nach Möglichkeit zu reduzieren.

ocomfd
quelle
7
Kommt drauf an was das conditionim Besonderen ist.
Bergi
4
Es gibt einen Grund, warum Misra die Verwendung von "break" und "continue" in Schleifen verbietet. Siehe auch: stackoverflow.com/q/3922599/476681
BЈовић
35
Ich denke, die Anzahl der Zeilen selbst ist nicht das eigentliche Problem. Es könnte in einer Zeile geschrieben werden. if (condition) break; Das Problem ist meiner Meinung nach die Lesbarkeit. Ich persönlich mag es nicht, eine Schleife zu unterbrechen, außer die Schleifenbedingung zu verwenden.
Joe
97
weil es 3 Codezeilen verbraucht Ein sehr, sehr, sehr schlechter Grund, einen Stil nicht zu mögen. IMO "Codezeilen verbrauchen" ist irgendwo zwischen irrelevant und eine gute Sache. Der Versuch, zu viel Logik in zu wenige Zeilen zu stopfen, führt zu unlesbarem Code und schwer zu findenden Fehlern.
Andrew Henle
3
Möglicherweise unpopuläre Meinung: for(int i=0;i<array.length && !condition;i++)Ist relativ ungewöhnlich und wird möglicherweise von jemandem übersehen, der nur den Code überfliegt. Dies ist möglicherweise ein guter Anwendungsfall für eine whileoder do whileSchleife, die in der Schleifendefinition häufig mehrere Unterbrechungsbedingungen enthält.
Jules

Antworten:

154

Diese beiden Beispiele, die Sie angegeben haben, sind funktional nicht gleichwertig. Im Original wird der Bedingungstest nach dem Abschnitt "Ein anderer Code" durchgeführt, während er in der modifizierten Version zuerst am Anfang des Schleifenkörpers durchgeführt wird.

Der Code sollte niemals mit dem einzigen Zweck umgeschrieben werden, die Anzahl der Zeilen zu verringern. Natürlich ist es ein schöner Bonus, wenn es so funktioniert, aber es sollte niemals auf Kosten der Lesbarkeit oder Korrektheit geschehen.

Pete
quelle
5
Ich verstehe Ihr Gefühl, aber ich habe genug alten Code beibehalten, in dem die Schleife mehrere hundert Zeilen lang ist und es mehrere bedingte Unterbrechungen gibt. Das Debuggen ist sehr schwierig, wenn erwartet wird, dass der Code die erwartete bedingte Unterbrechung erreicht, der vorherige Code jedoch zuerst die bedingte Unterbrechung ausführt. Wie gehst du mit diesem Code um? In kurzen Beispielen wie den oben genannten lässt sich ein allzu häufiges Szenario dieser Art leicht vergessen.
Berin Loritsch
84
@BerinLoritsch: Der richtige Weg, damit umzugehen, besteht darin, keine Schleifen zu haben, die mehrere hundert Zeilen lang sind!
Chris
27
@Walfrat: Wenn das Umschreiben keine Option ist, gibt es eigentlich keine Frage mehr. Allerdings können Sie normalerweise ziemlich sicher einen Refaktor vom Typ "Methode extrahieren" verwenden, um Codeblöcke zu verschieben, wodurch Ihre Hauptmethodenschleife kürzer und der logische Ablauf hoffentlich deutlicher wird. Es ist wirklich eine Sache von Fall zu Fall. Ich werde mich an meine allgemeine Regel halten, Methoden kurz zu halten und auf jeden Fall die Logik der nächsten Schleife kurz zu halten, aber ich möchte nicht versuchen, mehr als das im allgemeinen Sinne zu sagen ...
Chris
4
@ AndrewHenle Kürze ist Lesbarkeit, zumindest in dem Sinne, dass eine zunehmende Ausführlichkeit die Lesbarkeit und Wartbarkeit immer verringert. Das Reduzieren des LOC wird durch Erhöhen der Dichte und Erhöhen des Abstraktionsniveaus erreicht und ist sehr eng mit der Wartbarkeit korreliert, wie in anderen Q / A-Paaren an diesem Standort bestätigt.
Leushenko
5
@Joe ein Do-While-Konstrukt ist so selten, dass es für Trip-Entwickler anfällig ist, die diesen Code später pflegen müssen. Einige Entwickler haben sie noch nie gesehen! Ich bin mir nicht sicher, ob eine Weile die Lesbarkeit verbessern würde - wenn überhaupt, würde dies die Schwierigkeit erhöhen , den Code zu verstehen. Nur als Beispiel - meine aktuelle Codebasis ist ungefähr 15 Jahre alt und hat ungefähr 2 Millionen LOC. Rund fünfzig Entwickler haben es bereits angefasst. Es hat genau null do-while-Schleifen!
T. Sar - Reinstate Monica
51

Ich kaufe nicht das Argument, dass "es 3 Zeilen Code verbraucht" und daher schlecht ist. Immerhin könnte man es einfach so schreiben:

if (condition) break;

und nur eine Zeile verbrauchen.

Wenn das ifauf halbem Weg durch die Schleife erscheint, muss es natürlich als separater Test existieren. Wenn dieser Test jedoch am Ende des Blocks angezeigt wird, haben Sie eine Schleife erstellt, die an beiden Enden der Schleife einen Dauertest enthält, der möglicherweise die Komplexität des Codes erhöht. Beachten Sie jedoch, dass der if (condition)Test manchmal erst Sinn macht, nachdem der Block ausgeführt wurde.

Wenn dies jedoch nicht der Fall ist, verwenden Sie den anderen Ansatz:

for(int i=0;i<array.length && !condition;i++){
    //some other code
}

Die Exit-Bedingungen werden zusammengehalten, was die Dinge vereinfachen kann (insbesondere wenn " ein anderer Code " lang ist).

Hier gibt es natürlich keine richtige Antwort. Achten Sie also auf die Redewendungen der Sprache, die Kodierungskonventionen Ihres Teams usw. Alles in allem würde ich jedoch den Single-Test-Ansatz wählen, wenn dies funktional sinnvoll ist.

David Arno
quelle
14
@ jpmc26, besser? Nein. Gültiger alternativer Stil? Na sicher.
David Arno
6
Besser als schwieriger durcheinander zu bringen, wenn der Code später bearbeitet wird und keine nennenswerten Nachteile aufweist.
jpmc26
4
Es scheint ein schwaches Argument zu sein, sie zusammen zu halten, weil "Schleifeninhalte lang sein können". Wenn Ihr for-Inhalt schwer zu lesen ist, haben Sie andere Probleme als das Speichern der beiden Codezeilen.
BlueWizard
12
@ jpmc26 Stimme nicht zu. Zahnspangen sind überflüssiges Durcheinander in den kürzesten, einzeiligen Bedingungen, wie in dieser Antwort angegeben. Ohne ist eleganter und schont das Auge. Sie sind wie ein ternärer bedingter Operator. Ich habe nie vergessen, die Klammern hinzuzufügen, während ich sie in größere Anweisungsblöcke aufgeteilt habe. Ich füge doch schon neue Zeilen hinzu.
Benxyzzy
3
Es ist alles eine Stilvorliebe, aber wenn man argumentiert, dass es sicherer ist, würde ich dieser Argumentation nicht zustimmen. Ich selbst bevorzuge es ohne geschweifte Klammern und in der nächsten Zeile, aber das bedeutet nicht, dass es mehr oder weniger gültig ist als andere Stile
phflack
49

Diese Art von Frage hat schon fast so lange Diskussionen ausgelöst wie die Programmierung. Um meinen Hut in den Ring zu werfen, würde ich mich für die Version mit der breakBedingung entscheiden und hier ist der Grund (für diesen speziellen Fall):

Die forSchleife ist nur dazu da, die Iterationswerte in der Schleife anzugeben. Das Kodieren des Unterbrechungszustands innerhalb dessen trübt nur die logische IMO.

Durch breakdie Angabe eines separaten Elements wird deutlich, dass die Werte während der normalen Verarbeitung den in der forSchleife angegebenen Werten entsprechen und die Verarbeitung fortgesetzt werden sollte, sofern die Bedingung nicht vorliegt.

Als Hintergrund habe ich mit dem Codieren von a begonnen break, dann die Bedingung forjahrelang in die Schleife gestellt (unter der Leitung eines außergewöhnlichen Programmierers) und dann zum breakStil zurückgekehrt, weil er sich einfach viel sauberer anfühlte (und der vorherrschende Stil in war) die verschiedenen Codebücher, die ich konsumiert habe).

Es ist ein weiterer dieser heiligen Kriege am Ende des Tages - manche sagen, man sollte niemals künstlich aus einer Schleife ausbrechen, sonst ist es keine echte Schleife. Tun Sie, was Sie für lesbar halten (sowohl für sich selbst als auch für andere). Wenn Sie die Tastenanschläge wirklich reduzieren möchten, spielen Sie am Wochenende Code-Golf - Bier optional.

Robbie Dee
quelle
3
Wenn die Bedingung einen guten Namen hat, z. B. "found" (z. B. wenn Sie das Array nach etwas durchsuchen), empfiehlt es sich, den Namen in den Kopf der for-Schleife einzufügen.
user949300
1
Mir wurde gesagt, dass forSchleifen verwendet werden, wenn die Anzahl der Iterationen bekannt ist (z. B. wenn es keine Unterbrechungsbedingung gibt, aber das Ende des Arrays / der Liste) und whilewenn dies nicht der Fall ist . Dies scheint für eine whileSchleife der Fall zu sein . Wenn es um Lesbarkeit geht, ist das idx+=1Lesen in der Schleife viel sauberer als in einem if/elseBlock
Laiv
Sie können die break-Bedingung nicht einfach in die Schleife einfügen, wenn darauf Code folgt. Sie müssten also zumindest schreiben: "if (condition) {found = true; continue;}
gnasher729
Wir sind so an for(int i=0; i<something;i++)Loops gewöhnt, dass die Hälfte der Entwickler sich nicht wirklich daran erinnert, dass forLoops unterschiedlich verwendet werden können und es daher schwierig ist, den &&conditionTeil zu verstehen .
Ralf Kleberhoff
@RalfKleberhoff In der Tat. Ich habe viele neue Entwickler überrascht gesehen, dass die tripart-Ausdrücke alle optional sind. Ich kann mich nicht einmal an das letzte Mal erinnern, als ich for(;;)...
Robbie Dee
43

forSchleifen sind für die Iteration über etwas 1 - sie sind nicht nur lol-let-pull-some-random-stuff-from-the-body-und-put-them-in-the-loop-Header - die drei Ausdrücke haben sehr spezifische Bedeutungen:

  • Die erste dient zum Initialisieren des Iterators . Es kann ein Index, ein Zeiger, ein Iterationsobjekt oder was auch immer sein - solange es für die Iteration verwendet wird .
  • Die zweite dient der Überprüfung, ob wir das Ende erreicht haben.
  • Die dritte dient zum Vorrücken des Iterators.

Die Idee ist, die Iterationsbehandlung ( wie iteriert wird) von der Logik innerhalb der Schleife zu trennen ( was in jeder Iteration zu tun ist). In vielen Sprachen gibt es normalerweise eine For-Each-Schleife, die Sie von den Einzelheiten der Iteration befreit. Selbst wenn Ihre Sprache dieses Konstrukt nicht besitzt oder in Ihrem Szenario nicht verwendet werden kann, sollten Sie den Schleifenkopf trotzdem einschränken zur Iterationsbehandlung.

Sie müssen sich also fragen: Besteht Ihre Bedingung in Bezug auf die Iterationsbehandlung oder in Bezug auf die Logik in der Schleife? Wahrscheinlich handelt es sich um die Logik innerhalb der Schleife. Sie sollte daher innerhalb der Schleife und nicht im Header überprüft werden.

1 Im Gegensatz zu anderen Schleifen, die darauf warten, dass etwas passiert. Eine for/ foreachloop sollte das Konzept einer "Liste" von Elementen haben, auf denen iteriert werden soll - auch wenn diese Liste faul oder unendlich oder abstrakt ist.

Idan Arye
quelle
5
Dies war mein erster Gedanke, als ich die Frage las. Enttäuscht musste ich ein halbes Dutzend Antworten durchblättern, um jemanden zu sehen, der es sagt
Nemo
4
Ähm ... alle Schleifen sind für die Iteration - das bedeutet das Wort "Iteration" :-). Ich nehme an, Sie meinen so etwas wie "Iterieren über eine Sammlung" oder "Iterieren mit einem Iterator"?
Psmears
3
@psmears OK, vielleicht habe ich das falsche Wort gewählt - Englisch ist nicht meine Muttersprache, und wenn ich an Iteration denke, denke ich an "Iteration über etwas". Ich werde die Antwort reparieren.
Idan Arye
Ein Zwischenfall ist, wo die Bedingung so etwas ist someFunction(array[i]). Jetzt geht es in beiden Teilen der Bedingung um das, worüber Sie iterieren, und es wäre sinnvoll, sie &&im Schleifenkopf zu kombinieren .
Barmar
Ich respektiere Ihre Position, aber ich bin anderer Meinung. Ich denke, forSchleifen im Herzen sind Zähler, keine Schleifen über eine Liste, und dies wird durch die Syntax forfrüherer Sprachen (diese Sprachen hatten oft eine for i = 1 to 10 step 2Syntax und den stepBefehl) unterstützt - selbst wenn ein Anfangswert zulässig ist, der nicht der Index des ersten ist Element - Hinweise auf einen numerischen Kontext, keinen Listenkontext). Der einzige Anwendungsfall, von dem ich denke, dass er forSchleifen unterstützt , da er nur über eine Liste iteriert, ist die Parallelisierung. Dies erfordert jetzt ohnehin eine explizite Syntax, um den Code nicht zu beschädigen, der beispielsweise eine Sortierung vornimmt.
Logan Pickup
8

Ich empfehle die Version mit if (condition). Es ist lesbarer und einfacher zu debuggen. Das Schreiben von 3 zusätzlichen Codezeilen bricht Ihnen nicht die Finger, erleichtert es jedoch der nächsten Person, Ihren Code zu verstehen.

Aleksander
quelle
1
Nur wenn (wie bereits kommentiert) der Loop-Block nicht aus Hunderten von Codezeilen besteht und die darin enthaltene Logik keine Raketenwissenschaft ist. Ich denke, Ihr Argument ist naming thingsin Ordnung , aber ich vermisse so etwas wie richtig, um zu verstehen, was los ist und wann die Bruchbedingung erfüllt ist.
Laiv
2
@Laiv Wenn der Schleifenblock Hunderte von Codezeilen enthält, sind die Probleme größer als die Frage, wo die Unterbrechungsbedingung zu schreiben ist.
Aleksander
Aus diesem Grund habe ich vorgeschlagen, die Antwort in einen Kontext zu setzen, da dies nicht immer der Fall ist.
Laiv
8

Wenn Sie eine Sammlung durchlaufen, in der bestimmte Elemente übersprungen werden sollen, empfehle ich eine neue Option:

  • Filtern Sie Ihre Sammlung, bevor Sie sie wiederholen

Sowohl Java als auch C # machen dies relativ trivial. Es würde mich nicht wundern, wenn C ++ eine Möglichkeit hätte, einen ausgefallenen Iterator zu erstellen, um bestimmte Elemente zu überspringen, die nicht zutreffen. Dies ist jedoch nur dann wirklich eine Option, wenn Ihre bevorzugte Sprache dies unterstützt. Der Grund für die Verwendung breakliegt darin, dass Bedingungen dafür bestehen, ob Sie ein Element verarbeiten oder nicht.

Ansonsten gibt es einen sehr guten Grund, Ihre Bedingung in die for-Schleife aufzunehmen - vorausgesetzt, ihre anfängliche Auswertung ist korrekt.

  • Es ist einfacher zu erkennen, wann eine Schleife vorzeitig endet

Ich habe an Code gearbeitet, in dem Ihre for-Schleife einige hundert Zeilen mit mehreren Bedingungen und etwas komplexer Mathematik enthält. Die Probleme, auf die Sie dabei stoßen, sind:

  • break Befehle in der Logik sind schwer zu finden und manchmal überraschend
  • Das Debuggen erfordert das Durchlaufen jeder Iteration der Schleife, um wirklich zu verstehen, was vor sich geht
  • Für viele Codes sind keine leicht umkehrbaren Refactorings oder Komponententests verfügbar, um die Funktionsäquivalenz nach einem Umschreiben zu verbessern

In dieser Situation empfehle ich die folgenden Richtlinien in der Reihenfolge ihrer Präferenz:

  • Filtern Sie die Sammlung, um die Notwendigkeit einer breaknach Möglichkeit zu beseitigen
  • Machen Sie den bedingten Teil der for-Schleifenbedingungen
  • Platzieren Sie die Bedingungen so, dass sie breakmöglichst oben in der Schleife liegen
  • Fügen Sie einen mehrzeiligen Kommentar hinzu, der die Aufmerksamkeit auf die Unterbrechung lenkt und erklärt, warum sie vorhanden ist

Diese Richtlinien unterliegen immer der Richtigkeit Ihres Codes. Wählen Sie die Option, die die Absicht am besten darstellt und die Klarheit Ihres Codes verbessert.

Berin Loritsch
quelle
1
Der Großteil dieser Antwort ist in Ordnung. Aber ... ich kann sehen, wie das Filtern einer Sammlung die Notwendigkeit von continueAnweisungen eliminieren kann , aber ich sehe nicht, wie viel sie helfen break.
user949300
1
@ user949300 Ein takeWhileFilter kann breakAnweisungen ersetzen . Wenn Sie eine haben - Java zum Beispiel bekommt sie nur bei Jave 9 (nicht, dass es so schwer ist, sie selbst zu implementieren)
Idan Arye
1
In Java können Sie jedoch noch vor der Iteration filtern. Verwenden von Streams (1.8 oder höher) oder Apache Collections (1.7 oder früher).
Laiv
@IdanArye - Es gibt Add-On-Bibliotheken, die der Java-Streams-API (z. B. JOO-Lambda ) solche Funktionen hinzufügen.
Jules
@IdanArye hat vor Ihrem Kommentar noch nie von einem takeWhile gehört. Das explizite Verknüpfen des Filters mit einer Bestellung erscheint mir ungewöhnlich und hacky, da in einem "normalen" Filter jedes Element wahr oder falsch zurückgibt, unabhängig davon, was vorher oder nachher kommt. Auf diese Weise können Sie Dinge parallel ausführen.
user949300
8

Ich empfehle dringend, den Ansatz der geringsten Überraschung zu wählen, es sei denn, Sie erzielen einen erheblichen Vorteil, wenn Sie etwas anderes tun.

Die Leute lesen nicht jeden Buchstaben, wenn sie ein Wort lesen, und sie lesen nicht jedes Wort, wenn sie ein Buch lesen. Wenn sie geschickt darin sind, lesen sie die Umrisse von Wörtern und Sätzen und lassen ihr Gehirn den Rest ausfüllen .

Es ist also wahrscheinlich, dass der gelegentliche Entwickler davon ausgeht, dass dies nur ein Standard für eine Schleife ist, und es sich nicht einmal ansieht:

for(int i=0;i<array.length&&!condition;i++)

Wenn Sie diesen Stil trotzdem verwenden möchten, empfehle ich, die Parts zu ändern for(int i=0;i<und ;i++)dem Gehirn des Lesers mitzuteilen, dass dies ein Standard für Loop ist.


Ein weiterer Grund für if- breakist, dass Sie Ihren Ansatz nicht immer mit der forBedingung verwenden können. Sie müssen verwenden if- breakwenn die Abbruchbedingung innerhalb eines zu verstecken ist zu komplex forAnweisung oder stützt sich auf Variablen , die nur zugänglich innerhalb der sind forSchleife.

for(int i=0;i<array.length&&!((someWidth.X==17 && y < someWidth.x/2) || (y == someWidth.x/2 && someWidth.x == 18);i++)

Wenn Sie sich also für if- entscheiden break, sind Sie abgesichert. Wenn Sie sich jedoch für for-conditions entscheiden, müssen Sie eine Mischung aus if- breakund for-conditions verwenden. Um konsequent zu bleiben, werden Sie die Bedingungen hin und her bewegen müssen , um zwischen den for-Bedingungen und die if- breakimmer dann , wenn sich die Bedingungen ändern.

Peter
quelle
4

Bei Ihrer Transformation wird davon ausgegangen, dass das, was auch conditionimmer ausgewertet wird, truewenn Sie in die Schleife eintreten. Wir können die Richtigkeit in diesem Fall nicht beurteilen, da sie nicht definiert ist.

Nachdem es Ihnen hier gelungen ist, "Codezeilen zu reduzieren", schauen Sie sich das an

for(int i=0;i<array.length;i++){
    // some other code
    if(condition){
        break;
    }
    // further code
}

und wende die gleiche Transformation an, um zu gelangen

for(int i=0;i<array.length && !condition;i++){
    // some other code
    // further code
}

Was eine ungültige Transformation ist, da Sie jetzt bedingungslos dort vorgehen, further codewo Sie es zuvor nicht getan haben.

Ein viel sichereres Transformationsschema besteht darin , eine separate Funktion zu extrahieren some other codeund auszuwertencondition

bool evaluate_condition(int i) // This is an horrible name, but I have no context to improve it
{
     // some other code
     return condition;
}

for(int i=0;i<array.length && !evaluate_condition(i);i++){
    // further code
}
Caleth
quelle
4

TL; DR Tun Sie, was in Ihrem speziellen Fall am besten zu lesen ist

Nehmen wir folgendes Beispiel:

for ( int i = 1; i < array.length; i++ ) 
{
    if(something) break;
    // perform operations
}

In diesem Fall soll keiner der Codes in der for-Schleife ausgeführt werden, wenn dies somethingzutrifft. somethingDaher ist es sinnvoll, den Test von in das Bedingungsfeld zu verschieben.

for ( int i = 1; i < array.length && !something; i++ )

Dann wieder, je nachdem , ob somethingkann eingestellt werden , truebevor die Schleife, aber nicht in ihn, könnte dies könnte mehr Klarheit bieten:

if(!something)
{
    for ( int i = 1; i < array.length; i++ )
    {...}
}

Stellen Sie sich nun Folgendes vor:

for ( int i = 1; i < array.length; i++ ) 
{
    // perform operations
    if(something) break;
    // perform more operations
}

Wir senden dort eine sehr klare Botschaft. Wenn somethingdies während der Verarbeitung des Arrays wahr wird, brechen Sie an dieser Stelle den gesamten Vorgang ab. Um den Scheck in das Bedingungsfeld zu verschieben, müssen Sie Folgendes tun:

for ( int i = 1; i < array.length && !something; i++ ) 
{
    // perform operations
    if(!something)
    {
        // perform more operations
    }
}

Vermutlich ist die "Nachricht" verschwommen, und wir überprüfen den Fehlerzustand an zwei Stellen - was ist, wenn sich der Fehlerzustand ändert und wir einen dieser Stellen vergessen?

Es gibt natürlich viel mehr verschiedene Formen, die eine for-Schleife und Bedingung sein könnten, alle mit ihren eigenen Nuancen.

Schreiben Sie den Code so, dass er gut lesbar (dies ist offensichtlich etwas subjektiv) und kurz ist. Außerhalb eines drakonischen Satzes von Kodierungsrichtlinien haben alle "Regeln" Ausnahmen. Schreiben Sie, was Ihrer Meinung nach die beste Entscheidung ausdrückt, und minimieren Sie das Risiko zukünftiger Fehler.

Grimm der Opiner
quelle
2

Während es ansprechend sein kann, weil Sie alle Bedingungen im Schleifenkopf sehen und breakin großen Blöcken verwirrend sein können, ist eine forSchleife ein Muster, bei dem die meisten Programmierer eine Schleife über einen bestimmten Bereich erwarten.

Insbesondere in diesem Fall kann das Hinzufügen einer zusätzlichen Unterbrechungsbedingung zu Verwirrung führen und es ist schwerer zu erkennen, ob sie funktional gleichwertig ist.

Oft ist es eine gute Alternative, eine whileoder do {} while-Schleife zu verwenden, die beide Bedingungen überprüft, vorausgesetzt, Ihr Array enthält mindestens ein Element.

int i=0
do {
    // some other code
    i++; 
} while(i < array.length && condition);

Wenn Sie eine Schleife verwenden, die nur eine Bedingung überprüft und keinen Code aus dem Schleifenkopf ausführt, wird sehr deutlich, wann die Bedingung überprüft wird und wie der aktuelle Zustand und welcher Code mit Nebenwirkungen ist.

allo
quelle
Obwohl diese Frage bereits einige gute Antworten hatte, wollte ich dies ausdrücklich befürworten, da es einen wichtigen Punkt darstellt: Für mich for(...; i <= n; ...)macht es das Lesen des Codes tatsächlich schwieriger , wenn ich etwas anderes als ein einfaches gebundenes Überprüfen in der for-loop ( ) habe Anhalten und darüber nachdenken, was hier vor sich geht, und die Bedingung beim ersten Durchgang möglicherweise ganz verfehlen, weil ich nicht erwarte, dass sie da ist. Für mich bedeutet eine forSchleife, dass Sie einen Bereich durchlaufen. Wenn eine spezielle Exit-Bedingung vorliegt, sollte dies mit einem explizit angegeben werden if, oder Sie können ein verwenden while.
CompuChip
1

Das ist schlecht, da Code für Menschen gedacht ist - nicht idiomatische forSchleifen sind oft verwirrend, was bedeutet, dass sich die Fehler eher hier verstecken, aber der ursprüngliche Code konnte möglicherweise verbessert werden, während beide beibehalten wurden kurz und lesbar.

Wenn Sie einen Wert in einem Array suchen möchten (das bereitgestellte Codebeispiel ist etwas allgemein, aber es kann auch dieses Muster sein), können Sie einfach explizit versuchen, eine Funktion zu verwenden, die von einer Programmiersprache speziell für diesen Zweck bereitgestellt wird. Zum Beispiel (Pseudocode, da keine Programmiersprache angegeben wurde, variieren die Lösungen in Abhängigkeit von einer bestimmten Sprache).

array.find(item -> item.valid)

Dies sollte die Lösung spürbar verkürzen und gleichzeitig vereinfachen, da Ihr Code genau sagt, was Sie benötigen.

Konrad Borowski
quelle
1

Einige Gründe, die meiner Meinung nach besagen, sollten Sie nicht.

  1. Dies verringert die Lesbarkeit.
  2. Die Ausgabe ist möglicherweise nicht identisch. Dies kann jedoch mit einer do...whileSchleife (nicht while) erfolgen, da die Bedingung nach einer gewissen Codeausführung überprüft wird.

Aber obendrein bedenken Sie Folgendes:

for(int i=0;i<array.length;i++){

    // Some other code
    if(condition1){
        break;
    }

    // Some more code
    if(condition1){
        break;
    }

    // Some even more code
}

Können Sie es wirklich erreichen, indem Sie diese Bedingungen zu dieser forBedingung hinzufügen ?

Hart
quelle
Was ist " Eröffnung "? Meinen Sie " Meinung "?
Peter Mortensen
Was meinst du mit "Wenige Gründe in der Eröffnungsrede, die besagen, du sollst nicht" ? Können Sie näher darauf eingehen?
Peter Mortensen
0

Ich denke, das breakkann eine gute Idee sein, aber keine Codezeilen zu sparen.

Der Vorteil des Schreibens ohne breakist, dass die Nachbedingung der Schleife offensichtlich und explizit ist. Wenn Sie die Schleife beenden und die nächste Zeile des Programms ausführen, muss Ihre Schleifenbedingung i < array.length && !conditionfalsch gewesen sein. Daher war entweder igrößer oder gleich Ihrer Array-Länge oder conditionist wahr.

In der Version mit breakgibt es einen versteckten gotcha. Die Schleifenbedingung besagt, dass die Schleife beendet wird, wenn Sie für jeden gültigen Array-Index einen anderen Code ausgeführt haben. Tatsächlich gibt es jedoch eine breakAnweisung, die die Schleife vorher beenden könnte, und Sie werden sie erst sehen, wenn Sie den Schleifencode überprüfen sehr vorsichtig. Automatische statische Analysatoren, einschließlich der Optimierung von Compilern, könnten ebenfalls Probleme haben, die Nachbedingungen der Schleife zu ermitteln.

Dies war der ursprüngliche Grund, warum Edgar Dijkstra „Goto Considered Harmful“ schrieb. Es war keine Frage des Stils: Das Herausbrechen einer Schleife erschwert es, formell über den Status des Programms nachzudenken. Ich habe viele break;Aussagen in meinem Leben geschrieben, und als Erwachsener schrieb ich sogar eine goto(um aus mehreren Ebenen einer leistungskritischen inneren Schleife auszubrechen und dann mit einem anderen Zweig des Suchbaums fortzufahren). Es ist jedoch weitaus wahrscheinlicher, dass ich eine bedingte returnAnweisung aus einer Schleife heraus schreibe (die den Code nie nach der Schleife ausführt und daher die Post-Bedingungen nicht durcheinander bringen kann) als a break.

Nachsatz

Das Umgestalten des Codes, um dasselbe Verhalten zu erzielen, erfordert möglicherweise tatsächlich mehr Codezeilen. Ihr Refactoring kann für einen bestimmten anderen Code gültig sein oder wenn wir nachweisen können, dass dies conditionfalsch beginnt. Aber Ihr Beispiel ist im allgemeinen Fall gleichbedeutend mit:

if ( array.length > 0 ) {
  // Some other code.
}
for ( int i = 1; i < array.length && !condition; i++ ) {
  // Some other code.
}

Wenn ein anderer Code kein Einzeiler ist, können Sie ihn in eine Funktion verschieben, um sich nicht zu wiederholen, und dann haben Sie Ihre Schleife beinahe zu einer rekursiven Endfunktion umgestaltet.

Ich erkenne jedoch, dass dies nicht der Hauptpunkt Ihrer Frage war, und Sie haben es einfach gehalten.

Davislor
quelle
Das Problem ist nicht, dass die Unterbrechung es schwierig macht, über den Code zu streiten. Das Problem ist, dass die Unterbrechung an zufälligen Stellen den Code zu komplizierten Dingen macht. Wenn das tatsächlich benötigt wird, ist es nicht schwer, wegen der Unterbrechung zu streiten, sondern weil der Code komplizierte Dinge tun muss.
gnasher729
Hier ist der Grund, warum ich anderer Meinung bin: Wenn Sie wissen, dass es keine breakAnweisung gibt, wissen Sie, was die Schleifenbedingung ist und dass die Schleife stoppt, wenn diese Bedingung falsch ist. Wenn es eine gibt break? Dann gibt es mehrere Möglichkeiten, wie die Schleife enden könnte, und im Allgemeinen löst das Herausfinden, wann eine von ihnen die Schleife anhalten könnte, buchstäblich das Halteproblem. In der Praxis besteht meine größere Sorge darin, dass die Schleife wie eine Sache aussieht , aber wer auch immer den Code geschrieben hat, nachdem die Schleife einen Randfall übersehen hat, der in seiner komplexen Logik verborgen ist. Sachen im Loop-Zustand sind kaum zu übersehen.
Davislor
0

Ja, aber Ihre Syntax ist unkonventionell und daher schlecht (tm)

Hoffentlich unterstützt Ihre Sprache so etwas wie

while(condition) //condition being a condition which if true you want to continue looping
{
    do thing; //your conditionally executed code goes here
    i++; //you can use a counter if you want to reference array items in order of index
}
Ewan
quelle
2
Vielleicht hätte ich ein anderes Wort für "Bedingung" verwenden sollen. Ich habe es nicht wörtlich so gemeint, als würde es genau dieselbe Bedingung im Beispiel bedeuten
Ewan
1
Die for-Schleife ist nicht nur nicht unkonventionell, es gibt auch keinen Grund, warum eine while-Schleife dieser Form besser ist als eine entsprechende for-Schleife. Tatsächlich würden die meisten Leute sagen, dass es schlimmer ist, wie die Autoren der CPP Core Guidelines
Sean Burton,
2
Manche Leute hassen kreative Alternativen. Oder ein Problem anders betrachten als das, das sie im Kopf haben. Oder generell kluge Ärsche. Ich fühle deinen Schmerz, Bruder!
Martin Maat
1
@ Sean Entschuldigung Alter, ich weiß, dass die Leute es hassen, wenn ihnen gesagt wird, dass sie falsch liegen, aber ich verweise Sie auf es.77
Ewan
2
Dies whilebetont das Monster im Code - die Ausnahme, die sonst im Routine- / Alltagscode verborgen ist. Die Geschäftslogik steht im Vordergrund und im Zentrum, die Schleifenmechanik wird subsumiert. Es vermeidet die Reaktion "Warte eine Minute ..." auf die forSchleifenalternative. Diese Antwort behebt das Problem. absolut stimmberechtigt.
Radarbob
0

Wenn Sie befürchten for, dass eine übermäßig komplexe Aussage schwer lesbar ist, ist dies definitiv ein berechtigtes Anliegen. Die Verschwendung von vertikalem Raum ist ebenfalls ein Thema (wenn auch weniger kritisch).

(Persönlich mag ich die Regel nicht, dass ifAnweisungen immer geschweifte Klammern haben müssen, was größtenteils die Ursache für den verschwendeten vertikalen Raum ist. Beachten Sie, dass das Problem darin besteht, vertikalen Raum zu verschwenden . Die Verwendung von vertikalem Raum mit aussagekräftigen Linien sollte kein Problem sein.)

Eine Möglichkeit besteht darin, es in mehrere Zeilen aufzuteilen. Dies sollten Sie auf jeden Fall tun, wenn Sie sehr komplexe forAnweisungen mit mehreren Variablen und Bedingungen verwenden. (Dies ist für mich eine bessere Nutzung des vertikalen Raums, um so vielen Zeilen wie möglich etwas Sinnvolles zu geben.)

for (
   int i = 0, j = 0;
   i < array.length && !condition;
   i++, j++
) {
   // stuff
}

Ein besserer Ansatz könnte darin bestehen, zu versuchen, eine deklarativere und weniger imperativere Form zu verwenden. Dies hängt von der Sprache ab, oder Sie müssen möglicherweise Ihre eigenen Funktionen schreiben, um diese Funktion zu aktivieren. Es kommt auch darauf an, was conditioneigentlich ist. Vermutlich muss es sich irgendwie ändern können, je nachdem, was sich im Array befindet.

array
.takeWhile(condition)  // condition can be item => bool or (item, index) => bool
.forEach(doSomething); // doSomething can be item => void or (item, index) => void
Dave Cousineau
quelle
Das Aufteilen einer komplexen oder ungewöhnlichen forAussage in mehrere Zeilen ist die beste Idee in dieser ganzen Diskussion
user949300
0

Eine Sache, die vergessen wurde: Wenn ich von einer Schleife abbreche (nicht alle möglichen Iterationen ausführen, egal wie implementiert), möchte ich in der Regel nicht nur die Schleife verlassen, sondern auch wissen, warum dies passiert ist . Wenn ich zum Beispiel nach einem Element X suche, breche ich nicht nur die Schleife ab, sondern möchte auch wissen, dass X gefunden wurde und wo es sich befindet.

In dieser Situation ist ein Zustand außerhalb der Schleife ganz natürlich. Also fange ich an mit

bool found = false;
size_t foundIndex;

und in der Schleife werde ich schreiben

if (condition) {
    found = true;
    foundIndex = i;
    break;
}

mit der for-Schleife

for (i = 0; i < something && ! found; ++i)

Jetzt ist es ganz natürlich, den Zustand in der Schleife zu überprüfen. Ob es eine "break" -Anweisung gibt, hängt davon ab, ob sich in der Schleife eine weitere Verarbeitung befindet, die Sie vermeiden möchten. Wenn ein Teil der Verarbeitung erforderlich ist und ein anderer Teil nicht, haben Sie möglicherweise etwas Ähnliches

if (! found) { ... }

irgendwo in deiner Schleife.

gnasher729
quelle