Posso usare break per uscire da più cicli 'for' nidificati?


304

È possibile utilizzare la breakfunzione per uscire da più forloop nidificati ?

Se è così, come faresti a farlo? Puoi anche controllare quanti loop le breakuscite?


1
Invece di usare break o goto per uscire da più loop nidificati, è possibile racchiudere quella particolare logica in una funzione e utilizzare return per uscire da più loop nidificati. Ciò manterrà l'estetica del tuo codice e ti impedirà di usare goto, che è una cattiva pratica di programmazione.
Rishab Shinghal,

Risposte:


239

AFAIK, C ++ non supporta i loop di denominazione, come fanno Java e altre lingue. È possibile utilizzare un goto o creare un valore flag che si utilizza. Alla fine di ogni ciclo, controlla il valore del flag. Se è impostato su true, è possibile interrompere tale iterazione.


317
Non abbiate paura di usare un gotose questa è l'opzione migliore.
jkeys,

18
Sono un nuovo programmatore di C ++ (e uno senza alcuna formazione formale di programmazione), quindi dopo aver letto le dichiarazioni delle persone su goto. Sono titubante nell'usarlo nel timore che il mio programma possa improvvisamente esplodere e uccidermi. A parte questo, quando scrivevo programmi sul mio ti-83 (ovviamente in noiosa classe di matematica), le funzioni fornite dall'editor di base richiedevano l'uso di goto.
Falso l'

26
@Faken: utilizzano due tipi di programmatori goto: programmatori errati e programmatori pragmatici. I primi si spiegano da soli. Quest'ultimo, in cui ti adatteresti se scegli di usarli bene, usa un cosiddetto concetto "malvagio" quando è il minore dei (due) mali. Leggi questo per una migliore comprensione di alcuni concetti C ++ che potresti dover usare di volta in volta (macro, goto, preprocessore, array): parashift.com/c++-faq-lite/big-picture.html#faq-6.15
jkeys,

41
@Faken: non c'è niente di sbagliato nell'utilizzo di goto. Sta abusando di un goto che è problematico.
Tutti l'

28
@Hooked: Esatto, tranne per il fatto che l'uso gotoraramente è l'opzione migliore. Perché non mettere i loop nella loro funzione ( inline, se sei preoccupato per la velocità) e returnda questo?
sbi,

265

No, non rovinarlo con a break. Questa è l'ultima roccaforte rimasta per l'uso di goto.


19
Lo standard di codifica MISRA C ++ consente l'uso di goto per coprire questo tipo esatto di situazione.
Richard Corden,

11
I veri programmatori non hanno timore di usare goto. Fatto per 25 anni - nessun rimpianto - risparmiato un sacco di tempo di sviluppo.
Doug Null,

1
Sono d'accordo. goto è un must se non hai 8K pixel di larghezza e devi chiamare una sequenza di 30 chiamate del sistema operativo Windows.
Michaël Roy,

O un semplice "ritorno" inserendo il codice in una funzione.
Tara,

68

Solo per aggiungere una risposta esplicita usando lambdas:

  for (int i = 0; i < n1; ++i) {
    [&] {
      for (int j = 0; j < n2; ++j) {
        for (int k = 0; k < n3; ++k) {
          return; // yay we're breaking out of 2 loops here
        }
      }
    }();
  }

Naturalmente questo modello ha alcune limitazioni e ovviamente solo C ++ 11 ma penso che sia abbastanza utile.


7
Ciò potrebbe confondere il lettore nel pensare che il ritorno provochi la funzione in cui lambda ritorni e non la stessa lambda.
Xunie,

3
@Xunie: se il tuo loop è così complicato, che ti dimentichi di essere in una lambda, è tempo di metterli in una funzione diversa, ma per casi semplici, dovrebbe funzionare abbastanza bene.
MikeMB,

17
Penso che questa soluzione sia meravigliosa
tuket

Puoi catturare la lambda in un modello RIIA che gli dà un nome e lo chiama in dtor (aka scope gaurd). Ciò non aggiungerebbe alcun guadagno in termini di prestazioni, ma può migliorare la leggibilità e ridurre il rischio di perdere la parentesi di chiamata di funzione.
Red

56

Un altro approccio per uscire da un loop nidificato consiste nel fattorizzare entrambi i loop in una funzione separata e returnda quella funzione quando si desidera uscire.

Ovviamente, questo fa emergere l'altro argomento sul fatto che si debba mai esplicitamente returnda una funzione in qualsiasi luogo diverso dalla fine.


7
Questo è un problema C. Con RIAA il rimpatrio anticipato non è un problema poiché tutti i problemi associati al rimpatrio anticipato vengono gestiti correttamente.
Martin York,

4
Comprendo che la corretta applicazione di RIAA può risolvere il problema di pulizia delle risorse in C ++, ma ho visto che l'argomento filosofico contro il ritorno anticipato continua in altri ambienti e linguaggi. Un sistema su cui ho lavorato in cui lo standard di codifica proibiva il ritorno anticipato aveva funzioni disseminate di variabili booleane (con nomi simili continue_processing) che controllavano l'esecuzione di blocchi di codice più in basso nella funzione.
Greg Hewgill l'

21
Che cos'è la RIAA? È qualcosa come RAII? = D
jkeys l'

1
Dipende da quanti cicli ha e quanto è profondo il nido ... vuoi la pillola blu o la pillola rossa?
Matt,

34

break uscirà solo dal ciclo più interno che lo contiene.

Puoi usare goto per uscire da qualsiasi numero di loop.

Ovviamente il goto è spesso considerato dannoso .

è corretto utilizzare la funzione di interruzione [...]?

L'uso di break and goto può rendere più difficile ragionare sulla correttezza di un programma. Vedi qui per una discussione su questo: Dijkstra non era pazzo .


16
Una buona risposta in quanto spiega che il meme "goto è dannoso" è fortemente legato all'affermazione più generalizzata "l'interruzione del flusso di controllo è dannosa". Non ha senso dire "goto è dannoso", quindi voltarsi e raccomandare di usare breako return.
Pavel Minaev,

6
@Pavel: breake returnhai il vantaggio gotodi non dover cercare un'etichetta per trovare dove vanno. Sì, sotto sono una specie di goto, ma molto limitati. Sono molto più facili da decifrare dal cervello di un modello programmatore che non senza restrizioni goto. Quindi sono preferibili l'IMO.
sbi,

@sbi: vero, ma break non fa ancora parte della programmazione strutturata. È solo meglio tollerato di un goto.
jkeys,

2
@KarlVoigtland il link Dijkstra è obsoleto; questo sembra funzionare: blog.plover.com/2009/07
Aaron Brager,

3
Non c'è assolutamente nulla di sbagliato nell'usare goto in questa situazione. Un goto ben posizionato fa passi da gigante meglio e più leggibile rispetto a molte delle soluzioni contorte altrimenti proposte.
James,

22

Sebbene questa risposta sia già stata presentata, penso che un buon approccio sia quello di fare quanto segue:

for(unsigned int z = 0; z < z_max; z++)
{
    bool gotoMainLoop = false;
    for(unsigned int y = 0; y < y_max && !gotoMainLoop; y++)
    {
        for(unsigned int x = 0; x < x_max && !gotoMainLoop; x++)
        {
                          //do your stuff
                          if(condition)
                            gotoMainLoop = true;
        }
    }

}

5
che è buono ma ancora non leggibile, preferirei andare in quel caso
Петър Петров

2
questo rende il tuo codice "abbastanza" lento perché gotoMainLoopviene controllato ogni ciclo
Thomas

1
In questo caso, l'utilizzo del reale gotorende il core più leggibile e più performante.
Петър Петров

20

Cosa ne pensi di questo?

for(unsigned int i=0; i < 50; i++)
{
    for(unsigned int j=0; j < 50; j++)
    {
        for(unsigned int k=0; k < 50; k++)
        {
            //Some statement
            if (condition)
            {
                j=50;
                k=50;
            }
        }
    }
}

2
approccio interessante ma mi piace sicuramente il modo in cui ered @ inf.ig.sh lo gestisce. per (unsigned int y = 0; y <y_max &&! gotoMainLoop; y ++).
Andy,

15

Un esempio di codice che utilizza gotoe un'etichetta per uscire da un ciclo nidificato:

for (;;)
  for (;;)
    goto theEnd;
theEnd:

11

Un buon modo per uscire da diversi loop nidificati è di trasformare il codice in una funzione:

void foo()
{
    for(unsigned int i=0; i < 50; i++)
    {
        for(unsigned int j=0; j < 50; j++)
        {
            for(unsigned int k=0; k < 50; k++)
            {
                // If condition is true
                return;
            }
        }
    }
}

4
... che non è un'opzione se dobbiamo passare 10-20 variabili per impilare questa funzione.
Петър Петров

1
@ ПетърПетров quindi scegli una lambda che è anche migliore in quanto puoi definirla esattamente dove ne hai bisogno.
DarioP,

+1 per lambdas ma revisione nel nucleo del motore di gioco in cui anche un frame dello stack è ancora un collo di bottiglia. Mi dispiace dirlo, ma i lambda non sono così leggeri almeno in MSVC 2010.
Петър Петров

@ ПетърПетров Quindi cambia la coppia di funzioni in una classe e le variabili dello stack in membri privati.
Arthur Tacca,

Questo semplifica solo il codice già complesso :) A volte, goto è l'unica soluzione. Oppure, se si scrivono automi complessi con la documentazione "goto state X", allora goto sta effettivamente leggendo il codice come scritto nel documento. Inoltre, C # e go hanno entrambi goto con uno scopo: nessuna lingua è turing completa senza un goto, e goto è spesso lo strumento più usato per scrivere un traduttore intermedio o un codice simile ad un assembly.
Петър Петров,

5

goto può essere molto utile per rompere i loop nidificati

for (i = 0; i < 1000; i++) {
    for (j = 0; j < 1000; j++) {
        for (k = 0; k < 1000; k++) {
             for (l = 0; l < 1000; l++){
                ....
                if (condition)
                    goto break_me_here;
                ....
            }
        }
    }
}

break_me_here:
// Statements to be executed after code breaks at if condition

3

Il breakcomunicato termina l'esecuzione della vicina racchiude do, for, switcho whiledichiarazione in cui appare. Il controllo passa all'istruzione che segue l'istruzione terminata.

da msdn .


3

Penso che a gotosia valido in questa circostanza:

Per simulare un break/ continue, vorresti:

Rompere

for ( ;  ;  ) {
    for ( ;  ;  ) {
        /*Code here*/
        if (condition) {
            goto theEnd;
        }
    }
}
theEnd:

Continua

for ( ;  ; ) {
    for ( ;  ;  ) {
        /*Code here*/
        if (condition) {
            i++;
            goto multiCont;
        }
    }
    multiCont:
}

"Continua" non funzionerà qui, perché l' espressione dell'iterazione del primo ciclo non verrà eseguita
Fabio A.

Suppongo che l'iteratore per il primo ciclo sia i. Quindi i++prima del goto
JuliusAlphonso

0

Altri linguaggi come PHP accettano un parametro per break (ovvero break 2;) per specificare la quantità di livelli di loop nidificati da cui si vuole uscire, C ++ invece non lo fa. Dovrai risolverlo utilizzando un valore booleano impostato su false prima del ciclo, impostato su true nel ciclo se desideri interrompere, più un'interruzione condizionale dopo il ciclo nidificato, controllando se il valore booleano è impostato su true e rompere se sì.


0

So che questo è un vecchio post. Ma suggerirei una risposta un po 'logica e più semplice.

for(unsigned int i=0; i < 50; i++)
    {
        for(unsigned int j=0; j < conditionj; j++)
        {
            for(unsigned int k=0; k< conditionk ; k++)
            {
                // If condition is true

                j= conditionj;
               break;
            }
        }
    }

3
Questa non è una soluzione molto scalabile in quanto j = conditionjnon funzionerà se hai un predicato complesso invece di j < conditionj.
Sergey,

0

Rompere qualsiasi numero di loop di una sola boolvariabile, vedere di seguito:

bool check = true;

for (unsigned int i = 0; i < 50; i++)
{
    for (unsigned int j = 0; j < 50; j++)
    {
        for (unsigned int k = 0; k < 50; k++)
        {
            //Some statement
            if (condition)
            {
                check = false;
                break;
            }
        }
        if (!check)
        {
            break;
        }
    }
    if (!check)
    {
        break;
    }
}

In questo codice abbiamo break;tutti i loop.


0

Non sono sicuro che ne valga la pena, ma puoi emulare i loop denominati di Java con alcune semplici macro:

#define LOOP_NAME(name) \
    if ([[maybe_unused]] constexpr bool _namedloop_InvalidBreakOrContinue = false) \
    { \
        [[maybe_unused]] CAT(_namedloop_break_,name): break; \
        [[maybe_unused]] CAT(_namedloop_continue_,name): continue; \
    } \
    else

#define BREAK(name) goto CAT(_namedloop_break_,name)
#define CONTINUE(name) goto CAT(_namedloop_continue_,name)

#define CAT(x,y) CAT_(x,y)
#define CAT_(x,y) x##y

Esempio di utilizzo:

#include <iostream>

int main()
{
    // Prints:
    // 0 0
    // 0 1
    // 0 2
    // 1 0
    // 1 1

    for (int i = 0; i < 3; i++) LOOP_NAME(foo)
    {
        for (int j = 0; j < 3; j++)
        {
            std::cout << i << ' ' << j << '\n';
            if (i == 1 && j == 1)
                BREAK(foo);
        }
    }
}

Un altro esempio:

#include <iostream>

int main()
{
    // Prints: 
    // 0
    // 1
    // 0
    // 1
    // 0
    // 1

    int count = 3;
    do LOOP_NAME(foo)
    {
        for (int j = 0; j < 3; j++)
        {
            std::cout << ' ' << j << '\n';
            if (j == 1)
                CONTINUE(foo);
        }
    }
    while(count-- > 1);
}

-1
  while (i<n) {
    bool shouldBreakOuter = false;
    for (int j=i + 1; j<n; ++j) {
      if (someCondition) {
          shouldBreakOuter = true;
      }
    }

    if (shouldBreakOuter == true)
      break;

  }

-3

Puoi usare try ... catch.

try {
    for(int i=0; i<10; ++i) {
        for(int j=0; j<10; ++j) {
            if(i*j == 42)
                throw 0; // this is something like "break 2"
        }
    }
}
catch(int e) {} // just do nothing
// just continue with other code

Se devi uscire da più loop contemporaneamente, spesso è comunque un'eccezione.


1
Vorrei sapere il motivo per cui questa risposta ha ottenuto così tanti voti negativi.
hkBattousai,

6
@hkBattousai La soluzione ha voti negativi perché utilizza un'eccezione per controllare il flusso di esecuzione. Come suggerisce il nome, le eccezioni dovrebbero essere utilizzate solo in casi eccezionali.
Helio Santos,

4
@HelioSantos Non è una situazione eccezionale per la quale la lingua non fornisce una soluzione adeguata?
hkBattousai,

8
Le eccezioni sono lente.
Gordon,

2
L'impatto delle prestazioni del lancio è enorme per qualcosa che non è un errore irrecuperabile il 99% delle volte.
Michaël Roy,

-4

Uscire da un for-loop è un po 'strano per me, poiché la semantica di un for-loop in genere indica che verrà eseguita un determinato numero di volte. Tuttavia, non è male in tutti i casi; se stai cercando qualcosa in una collezione e vuoi rompere dopo averlo trovato, è utile. In C ++, tuttavia, non è possibile interrompere i loop nidificati; è in altre lingue attraverso l'uso di una pausa etichettata. Puoi usare un'etichetta e un goto, ma questo potrebbe farti venire il bruciore di stomaco durante la notte ..? Sembra l'opzione migliore però.


11
Non è affatto strano. Se stai iterando su una raccolta per cercare qualcosa (e non hai un modo più veloce di cercare), non ha senso finire il ciclo. (come esempio)
Joe,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.