Wie kann ich in Objective-C aus zwei verschachtelten for-Schleifen ausbrechen?


74

Ich habe zwei for-Schleifen, die so verschachtelt sind:

for(...) {
    for(...) {

    }
}

Ich weiß, dass es eine breakAussage gibt. Aber ich bin verwirrt darüber, ob es beide Schleifen durchbricht oder nur die, in der es aufgerufen wurde? Ich muss beide brechen, sobald ich sehe, dass es keinen Sinn macht, mehrmals zu iterieren.

Antworten:


94

break bricht aus einer Schleife aus, aber Sie können der äußeren Schleife ein Häkchen hinzufügen, das bricht, wenn die innere bricht.

bool dobreak = false;
for ( ..; !dobreak && ..; .. ) {
   for ( ... ) {
      if (...) {
         dobreak = true;
         break;
      }
   }
}

56
IMHO, mit goto ist viel sauberer.
Sigjuice

4
!dobreakist am falschen Ort; es sollte in den bedingten (für den zweiten Teil) und nicht in den Inkrementierungsschritt (für den dritten Teil von) gehen; Ich würde auch verwenden !dobreak && .., damit die anderen Bedingungen beim Brechen nicht bewertet werden müssen. Ich stimme mit sigjuice: gotoist nicht böse , wenn sie richtig verwendet wird , und dies ist ein Fall , in dem goto tut machen für eine bessere Code.
Ephemient

2
Dies würde immer noch möglichen Code in der äußeren Schleife ausführen, der nach der inneren Schleife kommen würde
Martijn

@Martijn - Dies kann behoben werden, indem das breakdurch eine continueAnweisung ersetzt wird. Trotzdem ist es eher nicht offensichtlich, worüber man sich Sorgen machen muss.
Ori Pessach

107

Wenn die Verwendung von goto den Code vereinfacht, ist dies angemessen.

for (;;) 
{
    for (;;) 
    {
        break; /* breaks inner loop */
    } 
    for (;;) 
    {
        goto outer; /* breaks outer loop */
    }
} 
outer:;

4
Um dies zu erweitern,for (;;) {for (;;) {break; /* breaks inner loop */} for (;;) {goto outer; /* breaks outer loop */}} outer:;
kurzlebig

7
(;;) sieht aus wie ein kleines Gesicht.
Zakdances

14

Die breakAnweisung bringt Sie nur aus der innersten Schleife heraus. Wenn Sie den zusätzlichen Aufwand für Code, Speicher und Leistung einer dedizierten Statusvariablen nicht möchten, empfehle ich, den Code in eine eigene Funktion oder Methode umzuwandeln und zu verwenden return, um aus allen Schleifen herauszukommen:

void do_lots_of_work(void)
{
  int i, j;

  for(i=0; i<10 ; i++)
  {
    for(j=0;j< 10; j++)
    {
     ..
     ..
     if(disaster_struck())
      return; /* Gets us out of the loops, and the function too. */
    }
  }
}

Ich wäre sehr überrascht, wenn ein Funktionsaufruf in einer Schleife genauso gut funktionieren würde wie eine dedizierte Statusvariable, insbesondere wenn Sie Argumente übergeben müssen, um auf Variablen in der äußeren Funktion zugreifen zu können. Vielleicht, wenn der Compiler die Funktion inline kopiert ...
Ephemient

6
Was meiner Meinung nach schlimmer ist, ist der Verlust der Fähigkeit, den Algorithmus zu verstehen, indem man ihn an einer Stelle liest. All dies, um ein Goto zu vermeiden?
Ori Pessach

Ich bin auch auf dieses Break / Continue-Problem gestoßen und möchte manchmal die Schleife 2-3 Level höher fortsetzen. IMHO ist es am besten, die verschachtelten Schleifen in eine Methode umzugestalten, damit die Methode zurückgeben kann, ob die übergeordnete Schleife fortgesetzt werden soll.
Jason

Was ist, wenn der Benutzer nur die Schleife und nicht die Funktion verlassen möchte?
Akshay J

Dies ist meiner Meinung nach die beste Lösung. Es ist eindeutig, was passiert, es ist leicht zu kontrollieren, es ist leicht aufzurufen, es ordnet keinen unnötigen Booleschen Wert zu und bewertet ihn nicht, und es verwendet keine gefährlichen alten Praktiken wie goto. Leistungsbedenken sollten auch in der Praxis nicht beachtet werden.
Ben Leggiero

9

Anders als die bereits erwähnte Flagvariable oder Springen Sie könnten werfen eine Objective-C Ausnahme:

@try {
  for() {
    for() {
       @throw ...
    }
  }
}
@catch{
  ...
}

5
Wow, gerade als ich meine Antwort ändern wollte, um darauf hinzuweisen, wie die Leute jede einzelne verschlungene Lösung vorschlagen werden, außer Ausnahmen für die Flusskontrolle zu verwenden, nur um einen Fehler zu vermeiden ...
Ori Pessach

4
@Ori Pessach Well Ausnahmen sind die modernen OO-Versionen von goto ;-)
Lothar

1
Zumindest räumen sie den Stapel gut auf.
Ori Pessach

2
Nach meinen Erfahrungen sind Ausnahmen sehr, sehr langsam und sollten nicht zur Abwicklung des Ausführungsflusses verwendet werden.
Natalie Adams

3
Recherchiere nach Ausnahmen in ObjC. In den Apple-Dokumenten heißt es ausdrücklich, dass der Leistungseinbruch groß ist, und sie sagen: "Die Cocoa-Frameworks sind im Allgemeinen nicht ausnahmesicher. Das allgemeine Muster lautet, dass Ausnahmen nur für Programmiererfehler reserviert sind und das Programm, das eine solche Ausnahme abfängt, bald danach beendet werden sollte . "
jpswain

7

Andere haben erwähnt, wie Sie ein Flag setzen oder ein verwenden können goto, aber ich würde empfehlen, Ihren Code so umzugestalten, dass die innere Schleife in eine separate Methode umgewandelt wird. Diese Methode kann dann ein Flag zurückgeben, um anzuzeigen, dass die äußere Schleife sollte break. Wenn Sie Ihre Methoden entsprechend benennen, ist dies viel besser lesbar.

for (int i = 0; i < 10; i++) {
   if (timeToStop(i)) break;
}

-(bool) timeToStop: (int) i {
    for (int j = 0; j < 10; j++) {
        if (somethingBadHappens) return true;
    }

    return false;
}

Pseudocode, nicht getestet, aber Sie bekommen die Idee.


3

Die break-Anweisung bricht nur aus der Schleife im Bereich aus, die die übergeordnete Schleife ist. Wenn Sie auch aus der zweiten Schleife ausbrechen möchten, können Sie eine boolesche Variable verwenden, die für beide Schleifen gilt

bool isTerminated = false;

for (...)
{
    if (!isTerminated)
    {
        for(...)
        {
            ...

            isTerminated = true;
            break;
        }
    }
    else
    {
        break;
    }
}

2

Ändern Sie den Zähler der oberen Schleife vor der Pause

for(i=0; i<10 ; i++)
  for(j=0;j< 10; j++){
     ..
     ..
     i = 10; 
     break;
  }

13
Ich mag das nicht so sehr. Das Risiko besteht darin, dass jemand an einer Stelle die Ausgangsbedingung ändert und die andere vergisst.
Johan Kotlinski

NSUInteger limit = 10; Dann:for(i=0; i<limit; i++) { for(j=0; j<10; j++) { .. .. i = limit; break; } }
Matt Mc

2

Der wahrscheinlich einfachste Weg ist die Verwendung einer "Flag" -Variablen

for(i=0; i<10 && (done==false); i++)
  for(j=0;j< 10; j++){
     ..
     ..
     if(...){done=true; break;}
  }

@Jonny diese werden fast-enumerationSchleifen in Obj-C
Albert Renshaw

2

Eine andere Lösung besteht darin, die zweite Schleife in einer Funktion herauszufiltern:

int i;

for(i=0; i<10 ; i++){
    if !innerLoop(i) {
        break;
    }
}

bool innerLoop(int i)
    int j;
    for(j=0;j< 10; j++){
        doSomthing(i,j);
        if(endcondtion){
            return false;
        }
    }
}

1

Die break-Anweisung bricht aus der innersten Schleife aus. Eine zusätzliche Test- und Unterbrechungsanweisung wäre erforderlich, um aus der äußeren Schleife auszubrechen.


0

Wenn eine Unterbrechung innerhalb einer Reihe verschachtelter Schleifen ausgeführt wird, wird nur die innerste Schleife beendet, in der die Unterbrechung ausgeführt wird. (Genau wie Standard C)


0

Genau wie die letzten, im Allgemeinen so:

for(i=0;i<a;i++){  
 for(j=0;j<a;j++){
  if(Something_goes_wrong){
   i=a;
   break;
   }
 }
}

0

Wie wäre es, wenn Sie diese True / False-Prüfung in eine Methode umwandeln und returnAnweisungen verwenden:

- (bool) checkTrueFalse: parameters{

   for ( ...) {
      for ( ... ) {

         if (...) {
            return true;
         }

      }
   }
   return false;
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.