Como quebrar loops aninhados?

96

Se eu usar uma breakinstrução, ela só quebrará o loop interno e preciso usar algum sinalizador para quebrar o loop externo. Mas se houver muitos loops aninhados, o código não terá uma boa aparência.

Existe alguma outra maneira de quebrar todos os loops? (Por favor, não use goto stmt.)

for(int i = 0; i < 1000; i++) {
   for(int j = 0; j < 1000; j++) {
       if(condition) {
            // both of the loops need to break and control will go to stmt2
       }
   }

}

stmt2
user966379
fonte
2
você pode tentar int i e int j antes de o loop começar e, então, com a condição torná-los 1001, o loop não itera o próximo.
Khurram Ijaz
Versão C ++: stackoverflow.com/questions/1257744/…
Ciro Santilli 郝海东 冠状 病 六四 事件 事件 法轮功

Respostas:

43

Usar:

if (condition) {
    i = j = 1000;
    break;
}
Peter Mortensen
fonte
49
Funciona, mas feio e não geral. E se alguém alterar o limite para 2.000 (suponha que o código seja mais longo, então você não percebe imediatamente)?
ugoren
1
@ugoren Também é tão simples então. e se você usasse um const int count =1000 , na inicialização global. ou como uma #definemacro.
Laksith
4
Como @ugoren aponta, não é uma solução geral. Uma vez que esta é a primeira resposta do Google para esta questão, seria bom se a solução geral tivesse sido selecionada. Bem, as pessoas estão acostumadas a verificar o # 2 de qualquer maneira.
BeeOnRope
1
Eu acho que só preciso de i = 1000?
Peter Wu
189

Não, não estrague a diversão com um break. Este é o último uso válido restante de goto;)

Do contrário, você pode usar sinalizadores para quebrar loops aninhados profundos.

Outra abordagem para quebrar um loop aninhado é fatorar ambos os loops em uma função separada e retornar dessa função quando quiser sair.

Resumido - para quebrar os loops aninhados:

  1. usar goto
  2. usar bandeiras
  3. fatorar loops em chamadas de função separadas

Não pude resistir a incluir xkcd aqui :)

insira a descrição da imagem aqui

fonte

Goto's são considerados prejudiciais, mas como muitas pessoas nos comentários sugerem que não precisa ser. Se usado com cautela, pode ser uma ótima ferramenta. Qualquer coisa usada com moderação é divertida.

Srikar Appalaraju
fonte
29
Goto é tão claro quanto você chegará aqui, sim. Definir a variável de saída para 1000 é ainda mais confuso.
correnos
3
Eu gostaria de acrescentar que os gotos não são explicitamente maus, eles apenas podem ser usados ​​para o mal. Acho que existem alguns casos, por exemplo este, em que são a melhor solução. "Não use gotos" é um bom começo, mas acho que o próximo passo na habilidade permite "Não use gotos de longo alcance".
Aatch
1
Eu gostaria de discordar disso: "Criar uma função resulta em quantidades exponenciais de adição / subtração do ponteiro da pilha". Se houver uma função local (estática) que é chamada em apenas um ponto no fluxo do programa, qualquer compilador decente irá embuti-la, e o código resultante é essencialmente o mesmo que com goto. Este é possivelmente o caso de otimização mais fácil para qualquer compilador.
DrV
1
A refatoração geralmente é a solução mais limpa. No entanto, se qualquer variável fora do loop for alterada durante o loop interno, as coisas ficarão complicadas. Uma possibilidade é passar a variável para a função interna por referência (ponteiro), mas isso pode confundir a otimização do compilador e produzir código extra desnecessário. Outra possibilidade é tornar essas variáveis ​​estáticas no nível do módulo, mas isso também não é muito bonito. Infelizmente, C não possui funções aninhadas, pois elas resolveriam este problema - a menos que você esteja disposto a usar gcc, que fornece uma extensão.
DrV
1
+1. E o Structured Programming de Donald E. Knuth com go to Statements ( wiki.c2.com/?StructuredProgrammingWithGoToStatements ) é um artigo interessante para equilibrar o de Dijkstra.
kmkaplan,
40
bool stop = false;
for (int i = 0; (i < 1000) && !stop; i++)
{
    for (int j = 0; (j < 1000) && !stop; j++)
    {
        if (condition)
            stop = true;
    }
}
Tung
fonte
A solução ainda incrementa ambas as variáveis ​​em um no intervalo, o que pode causar problemas
TheSola10
7
Pode-se definir "stop = true;" e então "break;". Então, logo após o fim do loop "for" interno, faça "if (stop) break;".
Jeff Grigg
34

Uma maneira é colocar todos os loops aninhados em uma função e retornar do loop mais interno no caso de uma necessidade de quebrar todos os loops.

function() 
{    
  for(int i=0; i<1000; i++)
  {
   for(int j=0; j<1000;j++)
   {
      if (condition)
        return;
   }
  }    
}
Jay
fonte
1
parece a melhor solução para mim
Luca Steeb,
20

Acho que gotovai resolver o problema

for(int i = 0; i < 1000; i++) {
    for(int j = 0; j < 1000; i++) {
        if (condition) {
            goto end;
        }
    }
}

end:
stmt2 
Renjith KN
fonte
@chikuba Recebi uma resposta de cprogramming.com/tutorial/goto.html e sua resposta não foi postada quando estou fazendo a mesma coisa, por isso não vejo sua postagem
Renjith KN
15

Você precisará de uma variável booleana, se quiser que seja legível:

bool broke = false;
for(int i = 0; i < 1000; i++) {
  for(int j = 0; j < 1000; i++) {
    if (condition) {
      broke = true;
      break;
    }
  }
  if (broke)
    break;
}

Se você quiser menos legível, pode participar da avaliação booleana:

bool broke = false;
for(int i = 0; i < 1000 && !broke; i++) {
  for(int j = 0; j < 1000; i++) {
    if (condition) {
      broke = true;
      break;
    }
  }
}

Por fim, você pode invalidar o loop inicial:

for(int i = 0; i < size; i++) {
  for(int j = 0; j < 1000; i++) {
    if (condition) {
      i = size;
      break;
    }
  }
}
Jack
fonte
4

Cuidado: esta resposta mostra uma construção realmente obscura.

Se você estiver usando o GCC, verifique esta biblioteca . Como no PHP, breakpode aceitar o número de loops aninhados que você deseja sair. Você pode escrever algo assim:

for(int i = 0; i < 1000; i++) {
   for(int j = 0; j < 1000; j++) {
       if(condition) {
            // break two nested enclosing loops
            break(2);
       }
   }
}
DaBler
fonte
E, por baixo do capô, está realmente usandogoto :)
jacobq
@ iX3 Posso usar o assembler embutido e a instrução jmp se isso ajudar.
DaBler
@DaBler, não sabia que você era o autor daquela biblioteca. Meu comentário não foi feito como feedback, mas sim observando que esta biblioteca usa o mesmo método da resposta aceita . Espero que seu comentário tenha sido uma piada, porque eu acho que usar um recurso de linguagem (até goto) é muito preferível para asm inline (específico da máquina, mais fácil de cometer um erro, mais difícil de ler, ...).
jacobq
3
for(int i = 0; i < 1000; i++) {
   for(int j = 0; j < 1000; i++) {
       if(condition) {
            goto end;
   }
} 

end:
Chikuba
fonte
3

Se você precisa dos valores de i e j, isso deve funcionar, mas com menos desempenho do que outros

for(i;i< 1000; i++){    
    for(j; j< 1000; j++){
        if(condition)
            break;
    }
    if(condition) //the same condition
        break;
}
Ali Eren Çelik
fonte
Observe que, se a condição depender de j, o valor da condição precisa ser armazenado de alguma forma para que ainda funcione.
SuperBastedMan
1
Você está certo, mas após o intervalo , o valor de j não muda, assim como o valor da condição.
Ali Eren Çelik
Esta é uma solução quebrada e não válida em geral. Ou j não está definido fora de seu loop ou for (int i = 0; i < 1000; i++) { for (int j = 0; j < 1000; j++) { if (workComplete[i][j]) break; /* do work */ workComplete[i][j] = true; } if (workComplete[i][j]) break; ... }vai sempre sair da espira externa depois da primeira iteração do circuito interno.
Chai T. Rex
-3
int i = 0, j= 0;

for(i;i< 1000; i++){    
    for(j; j< 1000; j++){
        if(condition){
            i = j = 1001;
            break;
        }
    }
}

Irá quebrar ambos os loops.

Khurram Ijaz
fonte
-3
for(int i = 0; i < 1000; i++) {
    for(int j = 0; j < 1000; i++) {
       if(condition) {
          func(para1, para2...);
          return;
       }
    }
}

func(para1, para2...) {
    stmt2;
}
Berlloon
fonte
Então, basicamente, você está dizendo que ele deve (1) fazer várias chamadas de função extras e então (2) girar pelo resto do tempo quando conditionse tornar falso. Ah, e o segundo loop será executado para sempre porque é incrementado em ivez de j,
opa
-4
i = 0;

do
{
  for (int j = 0; j < 1000; j++) // by the way, your code uses i++ here!
  {
     if (condition)
     {
       break;
     }
  }

  ++i;

} while ((i < 1000) && !condition);
guga
fonte