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?


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.


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.


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.


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.


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 .


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

}

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

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:

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

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

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 .


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

"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.


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

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.


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

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

    if (shouldBreakOuter == true)
      break;

  }

-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.


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.


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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.