Posso usar break para sair de vários loops aninhados 'for'?

304

É possível usar a breakfunção para sair de vários forloops aninhados ?

Se sim, como você faria isso? Você também pode controlar quantos ciclos as breaksaídas?

Faken
fonte
1
Em vez de usar break ou goto para sair de vários loops aninhados, você pode incluir essa lógica específica em uma função e usar return para sair de vários loops aninhados. Isso manterá a estética do seu código e impedirá que você use goto, o que é uma prática ruim de programação.
Rishab Shinghal 26/08/19

Respostas:

239

AFAIK, C ++ não suporta loops de nomeação, como Java e outras linguagens. Você pode usar um goto ou criar um valor de sinalizador usado. No final de cada loop, verifique o valor do sinalizador. Se estiver definido como true, você poderá interromper essa iteração.

Cullen Walsh
fonte
317
Não tenha medo de usar um gotose essa for a melhor opção.
jkeys
18
Eu sou um novo programador de C ++ (e um sem nenhum treinamento formal de programação), portanto, depois de ler sobre as reclamações das pessoas sobre ir embora. Eu hesito em usá-lo com medo de que meu programa possa explodir de repente e me matar. Fora isso, quando eu costumava escrever programas no meu ti-83 (na aula de matemática chata, é claro), as funções que o editor básico fornecia exigiam o uso de goto's.
Faken
26
@Faken: Dois tipos de programadores usam goto: programadores ruins e programadores pragmáticos. Os primeiros são auto-explicativos. O último, no qual você se encaixaria se optar por usá-los bem, usa o chamado conceito de "mal" quando é o menor dos (dois) males. Leia isso para entender melhor alguns conceitos de C ++ que você pode precisar usar de tempos em tempos (macros, goto's, pré-processador, matrizes): parashift.com/c++-faq-lite/big-picture.html#faq-6.15
jkeys 11/08/09
41
@ Fake: Não há nada de errado em usar goto. É abusar de um goto que é problemático.
Todo mundo
28
@ Hooked: Isso mesmo, exceto que o uso gotoraramente é a melhor opção. Por que não colocar os loops em sua própria função ( inlinese você está preocupado com a velocidade) e returncom isso?
SBI
265

Não, não estrague tudo com um break. Esta é a última fortaleza restante para o uso de goto.

Henk Holterman
fonte
19
O padrão de codificação MISRA C ++ permite o uso de goto para cobrir esse tipo exato de situação.
9139 Richard Corden
11
Programadores reais não têm medo de usar o goto. Feito isso por 25 anos - sem arrependimentos - economizou uma tonelada de tempo de desenvolvimento.
Doug Null
1
Concordo. o gotos é obrigatório se você não tiver 8K pixels de largura e precisar chamar uma sequência de 30 chamadas do sistema operacional Windows.
Michaël Roy
Ou um simples "retorno", colocando o código em uma função.
Tara
68

Apenas para adicionar uma resposta explícita 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
        }
      }
    }();
  }

Claro que esse padrão tem certas limitações e, obviamente, apenas C ++ 11, mas acho que é bastante útil.

Predelnik
fonte
7
Isso pode confundir o leitor ao pensar que o retorno faz com que a função retorne ao lambda, e não o próprio lambda.
Xunie
3
@ Xunie: Se o seu loop é tão complicado, que você esquece que está em uma lambda, é hora de colocá-los em uma função diferente, mas para casos simples, isso deve funcionar muito bem.
MikeMB
17
Eu acho que esta solução é bonito
tuket
Você pode capturar o lambda em um modelo RIIA que lhe dê um nome e o chama no dtor (também conhecido como scope gaurd). Isso não adicionaria nenhum ganho de desempenho, mas poderia melhorar a legibilidade e reduzir o risco de perder o parêntese da chamada de função.
Red.Wave
56

Outra abordagem para interromper um loop aninhado é fatorar os dois loops em uma função separada e a returnpartir dessa função quando você deseja sair.

Obviamente, isso traz outro argumento sobre se você deve explicitamente alguma vez returnde uma função em outro lugar que não seja o final.

Greg Hewgill
fonte
7
Esse é um problema em C. Com o RIAA, o retorno antecipado não é um problema, pois todos os problemas associados ao retorno antecipado são tratados corretamente.
Martin Iorque
4
Entendo que a aplicação adequada do RIAA pode resolver o problema de limpeza de recursos em C ++, mas vi o argumento filosófico contra o retorno precoce continuar em outros ambientes e linguagens. Um sistema em que trabalhei em que o padrão de codificação proibia o retorno antecipado tinha funções repletas de variáveis ​​booleanas (com nomes como continue_processing) que controlavam a execução de blocos de código mais abaixo na função.
Greg Hewgill
21
O que é o RIAA? Isso é algo como RAII? = D
jkeys
1
Depende de quantos loops ele tem e a profundidade do ninho ... você quer a pílula azul ou a vermelha?
Matt
34

break sairá apenas do loop mais interno que o contém.

Você pode usar o goto para interromper qualquer número de loops.

É claro que o goto é frequentemente considerado nocivo .

é apropriado usar a função de interrupção [...]?

Usar break e goto pode dificultar o raciocínio sobre a correção de um programa. Veja aqui uma discussão sobre isso: Dijkstra não era louco .

Karl Voigtland
fonte
16
Uma boa resposta, na medida em que explica que o meme "goto é prejudicial" está fortemente ligado à afirmação mais generalizada "interrupção do fluxo de controle é prejudicial". Não faz sentido dizer "ir é prejudicial", e depois se virar e recomendar o uso de breakou return.
Pavel Minaev
6
@ Pavel: breake returntenha a vantagem de gotonão precisar procurar um rótulo para descobrir para onde ele vai. Sim, por baixo eles são algum tipo goto, mas muito restrito. Eles são muito mais fáceis de decifrar pelo cérebro de correspondência de padrões de um programador do que pelo irrestrito goto. Então IMO são preferíveis.
SBI
@sbi: Verdade, mas o intervalo ainda não faz parte da programação estruturada. É apenas melhor tolerado do que a goto.
jkeys
2
@KarlVoigtland, o link Dijkstra está desatualizado; isso parece estar funcionando: blog.plover.com/2009/07
Aaron
3
Não há absolutamente nada de errado em usar o goto nessa situação. Um goto bem colocado é aos trancos e barrancos melhor e mais legível do que muitas das soluções distorcidas propostas de outra maneira.
James
22

Embora essa resposta já tenha sido apresentada, acho que uma boa abordagem é fazer o seguinte:

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;
        }
    }

}
vigor
fonte
5
o que é bom, mas ainda não é tão legível, eu prefiro Goto, nesse caso
Петър Петров
2
isso torna seu código "bastante" lento, porque ele gotoMainLoopé verificado a cada ciclo #
Thomas
1
Nesse caso, usar o real gototorna o núcleo mais legível e com melhor desempenho.
Петър Петров
20

Que tal agora?

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;
            }
        }
    }
}
jebeaudet
fonte
2
abordagem interessante, mas definitivamente gosto da maneira como o ered @ inf.ig.sh lida com isso. for (não assinado int y = 0; y <y_max &&! gotoMainLoop; y ++).
Andy
15

Um exemplo de código usando gotoe um rótulo para interromper um loop aninhado:

for (;;)
  for (;;)
    goto theEnd;
theEnd:
Hélio Santos
fonte
11

Uma boa maneira de interromper vários loops aninhados é refatorar seu código em uma função:

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;
            }
        }
    }
}
Deqing
fonte
4
... o que não é uma opção se tivermos que passar de 10 a 20 variáveis ​​para enquadrar esta função na pilha.
285133 #
1
@ ПетърПетров então opte por um lambda que também é melhor, pois você pode defini-lo exatamente onde precisar.
DarioP
+1 para lambdas, mas revisão no núcleo do mecanismo de jogo em que mesmo um quadro de pilha ainda é um gargalo. Desculpe dizer, mas lambdas não são tão leves pelo menos em MSVC 2010.
Петър Петров
@ ПетърПетров Em seguida, altere o par de funções em uma classe e as variáveis ​​de pilha em membros privados.
precisa
Isso só complica demais o código já complexo :) Às vezes, o goto é a única solução. Ou se você escreve autômatos complexos com a documentação "goto state X", goto está realmente fazendo o código ser lido como está escrito no documento. Além disso, o C # e o go têm ambos com um objetivo: nenhum idioma fica completo sem um goto, e os gotos são frequentemente as ferramentas mais usadas para escrever um tradutor intermediário ou código semelhante a assembly.
Петър Петров
5

Goto pode ser muito útil para quebrar loops aninhados

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
Azeemali Hashmani
fonte
3

A breakdeclaração termina a execução do mais próximo delimitador do, for, switch, ou whiledeclaração na qual ele aparece. O controle passa para a instrução que segue a instrução finalizada.

do msdn .

Roubar
fonte
3

Eu acho que a gotoé válido nesta circunstância:

Para simular um break/ continue, você deseja:

Pausa

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

Continuar

for ( ;  ; ) {
    for ( ;  ;  ) {
        /*Code here*/
        if (condition) {
            i++;
            goto multiCont;
        }
    }
    multiCont:
}
JuliusAlphonso
fonte
"Continuar" não funcionará lá, porque a expressão da iteração do primeiro loop não será executada
Fabio A.
Estou assumindo que o iterador para o primeiro loop é i. Por isso, i++antes do goto
JuliusAlphonso /
0

Outras linguagens como o PHP aceitam um parâmetro para break (isto é, break 2;) para especificar a quantidade de níveis de loop aninhados dos quais você deseja interromper, o C ++, no entanto, não. Você precisará resolver isso usando um booleano que você definiu como false antes do loop, definido como true no loop, se você deseja interromper, além de uma quebra condicional após o loop aninhado, verificando se o booleano foi definido como true e quebre se sim.

Patrick Glandien
fonte
0

Eu sei que este é um post antigo. Mas eu sugeriria uma resposta um pouco lógica e mais simples.

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;
            }
        }
    }
Aditya Jagtap
fonte
3
Essa não é uma solução muito escalável, pois j = conditionjnão funcionará se você tiver um predicado complexo em vez de j < conditionj.
Sergey
0

Quebre qualquer número de loops por apenas uma boolvariável, veja abaixo:

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;
    }
}

Neste código, break;todos os loops.

Vikas Bansal
fonte
0

Não tenho certeza se vale a pena, mas você pode emular os loops nomeados do Java com algumas macros simples:

#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

Exemplo de uso:

#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);
        }
    }
}

Outro exemplo:

#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);
}
HolyBlackCat
fonte
-1
  while (i<n) {
    bool shouldBreakOuter = false;
    for (int j=i + 1; j<n; ++j) {
      if (someCondition) {
          shouldBreakOuter = true;
      }
    }

    if (shouldBreakOuter == true)
      break;

  }
MEnnabah
fonte
-3

Você pode usar 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 você precisar interromper vários loops de uma só vez, geralmente é uma exceção.

lawilog
fonte
1
Eu gostaria de saber o motivo pelo qual esta resposta recebe tantos votos negativos.
hkBattousai
6
@hkBattousai A solução tem votos negativos, porque está usando uma exceção para controlar o fluxo de execução. Como o nome sugere, as exceções devem ser usadas apenas em casos excepcionais.
Helio Santos
4
@HelioSantos Não é uma situação excepcional para a qual o idioma não está fornecendo a solução adequada?
precisa saber é o seguinte
8
As exceções são lentas.
Gordon
2
O impacto no desempenho do lançamento é enorme para algo que não é um erro irrecuperável em 99% das vezes.
Michaël Roy 16/06
-4

Quebrar um loop for é um pouco estranho para mim, uma vez que a semântica de um loop for indica que ele executará um número especificado de vezes. No entanto, não é ruim em todos os casos; se você estiver procurando por algo em uma coleção e quiser quebrar depois de encontrá-lo, é útil. A quebra de loops aninhados, no entanto, não é possível em C ++; está em outros idiomas através do uso de uma quebra rotulada. Você pode usar um rótulo e ir embora, mas isso pode causar azia à noite ..? Parece ser a melhor opção.

Nick Lewis
fonte
11
Não é nada estranho. Se você estiver repetindo uma coleção para procurar algo (e não tiver uma maneira mais rápida de pesquisar), não há sentido em terminar o loop. (como um exemplo)
Joe