Rompendo um loop aninhado

216

Se eu tenho um loop for aninhado em outro, como posso sair eficientemente dos dois loops (internos e externos) da maneira mais rápida possível?

Eu não quero ter que usar um booleano e depois dizer ir para outro método, mas apenas para executar a primeira linha de código após o loop externo.

Qual é uma maneira rápida e agradável de fazer isso?

Eu estava pensando que as exceções não são baratas / devem ser lançadas apenas em uma condição verdadeiramente excepcional etc. Portanto, não acho que essa solução seja boa do ponto de vista de desempenho.

Eu não acho certo tirar proveito dos recursos mais recentes do .NET (métodos anon) para fazer algo que é bastante fundamental.

GurdeepS
fonte
Eu só queria ter certeza: por que você quer fazer isso?
Jon Limjap
3
Por que você não deseja usar um booleano? O que há de errado em fazer isso?
21420 Anthony
No VB.net, você pode agrupar uma instrução try / finalmente (sem captura) em torno de um número arbitrário de loops; em seguida, "exit try" sairá de todos eles a qualquer momento.
Brain2000

Respostas:

206

Bem, gotomas isso é feio e nem sempre é possível. Você também pode colocar os loops em um método (ou um método não) e usá return-lo para voltar ao código principal.

    // goto
    for (int i = 0; i < 100; i++)
    {
        for (int j = 0; j < 100; j++)
        {
            goto Foo; // yeuck!
        }
    }
Foo:
    Console.WriteLine("Hi");

vs:

// anon-method
Action work = delegate
{
    for (int x = 0; x < 100; x++)
    {
        for (int y = 0; y < 100; y++)
        {
            return; // exits anon-method
        }
    }
};
work(); // execute anon-method
Console.WriteLine("Hi");

Observe que no C # 7 devemos obter "funções locais", o que (sintaxe tbd etc) significa que deve funcionar algo como:

// local function (declared **inside** another method)
void Work()
{
    for (int x = 0; x < 100; x++)
    {
        for (int y = 0; y < 100; y++)
        {
            return; // exits local function
        }
    }
};
Work(); // execute local function
Console.WriteLine("Hi");
Marc Gravell
fonte
49
Nesse tipo de situação, eu não acho que usar goto seja pior do que o uso normal de algo como break (afinal eles são apenas ramificações incondicionais de um rótulo, é apenas que com break o rótulo está implícito).
Greg Beech
37
às vezes goto é menos maligno do que as alternativas #
7898
7
@BeowulfOF - break quebrará apenas o loop interno, não os loops interno e externo.
22411 Greg Beech
36
Goto em si não é feio. O que é feio é abusar de goto, o que resulta em código de espaguete. Usar o goto para sair do loop aninhado é perfeitamente aceitável. Além disso, observe que todas as interrupções, continuações e retornos, do ponto de vista da programação estrutural, são dificilmente melhores do que ir ao fundo - basicamente são a mesma coisa, apenas em embalagens mais agradáveis. É por isso que as linguagens estruturais puras (como o Pascal original) carecem dos três.
El.pescado 4/04
11
@ el.pescado está totalmente certo: muitos codificadores foram enganados em acreditar que gotosão prejudiciais em si , enquanto simplesmente é o instrumento correto para fazer algumas coisas, e como muitos instrumentos podem ser mal utilizados. Essa aversão religiosa contra gotoé francamente bastante estúpida e definitivamente não científica.
o0 '.
96

A adaptação de C # da abordagem frequentemente usada em C - define o valor da variável do loop externo fora das condições do loop (ou seja, para loop usando a variável int INT_MAX -1geralmente é uma boa escolha):

for (int i = 0; i < 100; i++)
{
    for (int j = 0; j < 100; j++)
    {
        if (exit_condition)
        {
            // cause the outer loop to break:
            // use i = INT_MAX - 1; otherwise i++ == INT_MIN < 100 and loop will continue 
            i = int.MaxValue - 1;
            Console.WriteLine("Hi");
            // break the inner loop
            break;
        }
    }
    // if you have code in outer loop it will execute after break from inner loop    
}

Como a nota no código diz break, não irá pular magicamente para a próxima iteração do loop externo - portanto, se você tiver código fora do loop interno, essa abordagem exige mais verificações. Considere outras soluções nesse caso.

Essa abordagem trabalha com forewhile voltas, mas não funciona para foreach. No caso de foreachvocê não ter acesso ao código ao enumerador oculto, não poderá alterá-lo (e mesmo que IEnumeratornão possa ter algum método "MoveToEnd").

Agradecimentos aos autores dos comentários embutidos :
i = INT_MAX - 1sugestão de Meta
for / foreachcomentário de ygoe .
Adequado IntMaxpor jmbpiano
observação sobre código após loop interno por blizpasta

Nils Pipenbrinck
fonte
@DrG: não funcionará em c # como "A instrução break encerra o loop mais próximo ou a instrução switch em que aparece". (msdn)
blizpasta
1
@blizpasta Então? Se ele tornar a condição no loop externo falsa (como ele fez), sairá de ambas.
23311 Patrick
51
você deve usar i = INT_MAX - 1; caso contrário, i ++ == INT_MIN <100 e o loop continuará
Meta:
6
alguma idéia sobre foreach?
Ktutnik
4
@ktutnik Não funcionará com o foreach porque você não terá acesso ao código do enumerador oculto. O IEnumerator também não possui algum método "MoveToEnd".
ygoe 27/06
39

Esta solução não se aplica ao C #

Para as pessoas que encontraram essa pergunta por outros idiomas, Javascript, Java e D permitem quebras rotuladas e continuam :

outer: while(fn1())
{
   while(fn2())
   {
     if(fn3()) continue outer;
     if(fn4()) break outer;
   }
}
BCS
fonte
40
é triste que isso não possa ser feito com c #, pois em muitas vezes produzia código mais limpo.
Rickard
17
Na verdade, fiquei empolgado por um segundo até perceber que isso não era para c #. :(
Arvo Bowen
1
Esse conceito também é para o PowerShell , caso alguém encontre o problema lá. (Eles apenas colocam dois pontos na frente do nome do rótulo.) Agora eu sei que isso não é específico do PowerShell ... Essa sintaxe seria incompatível com os rótulos goto disponíveis em C #. PHP usa outra coisa: quebra 3; Coloque o número de níveis após a declaração de intervalo.
ygoe 27/06
1
Esta é java não c #
Luca Ziegler
Para aqueles que gostariam de ver esse recurso em C #, coloque seus parabéns aqui, por favor 😉 github.com/dotnet/csharplang/issues/869#issuecomment-326606003
m93a
28

Use uma proteção adequada no circuito externo. Coloque a proteção no circuito interno antes de quebrar.

bool exitedInner = false;

for (int i = 0; i < N && !exitedInner; ++i) {

    .... some outer loop stuff

    for (int j = 0; j < M; ++j) {

        if (sometest) {
            exitedInner = true;
            break;
        }
    }
    if (!exitedInner) {
       ... more outer loop stuff
    }
}

Ou, melhor ainda, abstraia o loop interno em um método e saia do loop externo quando ele retornar false.

for (int i = 0; i < N; ++i) {

    .... some outer loop stuff

    if (!doInner(i, N, M)) {
       break;
    }

    ... more outer loop stuff
}
tvanfosson
fonte
4
Exceto pelo OP, "Não quero usar um booleano".
LeopardSkinPillBoxHat
Muito pascal-ish ... eu provavelmente preferiria ir embora, embora normalmente os evite como uma praga.
Jonathan Leffler
21

Não me cite, mas você pode usar o goto conforme sugerido no MSDN. Existem outras soluções, como a inclusão de um sinalizador verificado em cada iteração dos dois loops. Finalmente, você pode usar uma exceção como uma solução realmente pesada para o seu problema.

VAMOS PARA:

for ( int i = 0; i < 10; ++i ) {
   for ( int j = 0; j < 10; ++j ) {
      // code
      if ( break_condition ) goto End;
      // more code
   }
}
End: ;

Doença:

bool exit = false;
for ( int i = 0; i < 10 && !exit; ++i ) {
   for ( int j = 0; j < 10 && !exit; ++j ) {
      // code
      if ( break_condition ) {
         exit = true;
         break; // or continue
      }
      // more code
   }
}

Exceção:

try {
    for ( int i = 0; i < 10 && !exit; ++i ) {
       for ( int j = 0; j < 10 && !exit; ++j ) {
          // code
          if ( break_condition ) {
             throw new Exception()
          }
          // more code
       }
    }
catch ( Exception e ) {}
David Rodríguez - dribeas
fonte
2
estas são todas as soluções hacky onde seria super clean ao fator de apenas em um método e usar retorno precoce
Dustin Getz
3
:) certo, essa é uma solução simples, mas você terá que passar todos os dados locais necessários para o método como argumentos ... Este é um dos poucos lugares em que o goto pode ser a solução apropriada
David Rodríguez - dribeas
O método Condition nem funciona porque "mais código" será executado uma vez após sair do loop interno antes de sair do loop externo. O método GOTO funciona, mas faz exatamente o que o pôster disse que eles não querem fazer. O método Exception funciona, mas é mais feio e mais lento que o GOTO.
Programador Windows
Eu destacaria esse ponto e vírgula após o rótulo. Dessa forma, esse rótulo pode estar no final de um bloco. +1
Tamas Hegedus
@Windowsprogrammer O OP perguntou: "Qual é uma maneira rápida e agradável de fazer isso?" Ir é a solução preferida: limpa e concisa, sem envolver um método separado.
Suncat2000
16

É possível refatorar o loop for aninhado em um método privado? Dessa forma, você pode simplesmente 'retornar' do método para sair do loop.

NoizWaves
fonte
Com o benefício de tornar seu método original mais curto :-)
Martin Capodici
Os lambdas do C ++ 11 facilitam isso em alguns casos: [&] { ... return; ... }();
BCS
14

Parece-me que as pessoas não gostam muito de uma gotodeclaração, então senti a necessidade de esclarecer um pouco isso.

Acredito que as 'emoções' que as pessoas gotoacabam se resumindo à compreensão do código e (equívocos) sobre possíveis implicações no desempenho. Antes de responder à pergunta, portanto, primeiro vou entrar em alguns detalhes de como ela é compilada.

Como todos sabemos, o C # é compilado para IL, que é compilado para o assembler usando um compilador SSA. Darei algumas idéias sobre como tudo isso funciona e, em seguida, tentarei responder à pergunta em si.

De C # para IL

Primeiro, precisamos de um pedaço de código C #. Vamos começar simples:

foreach (var item in array)
{
    // ... 
    break;
    // ...
}

Vou fazer isso passo a passo para lhe dar uma boa idéia do que acontece sob o capô.

Primeira tradução: de foreachpara o forloop equivalente (Nota: estou usando uma matriz aqui, porque não quero entrar em detalhes de IDisposable - nesse caso, eu também teria que usar um IEnumerable):

for (int i=0; i<array.Length; ++i)
{
    var item = array[i];
    // ...
    break;
    // ...
}

Segunda tradução: o fore breaké traduzido em um equivalente mais fácil:

int i=0;
while (i < array.Length)
{
    var item = array[i];
    // ...
    break;
    // ...
    ++i;
}

E terceira tradução (isso é equivalente ao código IL): mudamos break e whileem uma agência:

    int i=0; // for initialization

startLoop:
    if (i >= array.Length) // for condition
    {
        goto exitLoop;
    }
    var item = array[i];
    // ...
    goto exitLoop; // break
    // ...
    ++i;           // for post-expression
    goto startLoop; 

Embora o compilador faça essas coisas em uma única etapa, ele fornece informações sobre o processo. O código IL que evolui do programa C # é o tradução literal do último código C #. Você pode ver por si mesmo aqui: https://dotnetfiddle.net/QaiLRz (clique em 'ver IL')

Agora, uma coisa que você observou aqui é que, durante o processo, o código se torna mais complexo. A maneira mais fácil de observar isso é pelo fato de precisarmos de mais e mais códigos para realizar a mesma coisa. Você também pode argumentar que foreach,for , whilee breaksão realmente curtos mãos para goto, que é parcialmente verdadeiro.

De IL para Assembler

O compilador .NET JIT é um compilador SSA. Não vou entrar em todos os detalhes do formulário SSA aqui e como criar um compilador otimizador, é demais, mas posso fornecer um entendimento básico sobre o que acontecerá. Para uma compreensão mais profunda, é melhor começar a ler sobre a otimização de compiladores (eu gosto deste livro para uma breve introdução: http://ssabook.gforge.inria.fr/latest/book.pdf ) e LLVM (llvm.org) .

Todo compilador de otimização depende do fato de que o código é fácil e segue padrões previsíveis . No caso dos loops FOR, usamos a teoria dos grafos para analisar ramificações e, em seguida, otimizamos coisas como cycli em nossas ramificações (por exemplo, ramificações para trás).

No entanto, agora temos ramificações avançadas para implementar nossos loops. Como você deve ter adivinhado, esta é realmente uma das primeiras etapas que o JIT corrigirá, assim:

    int i=0; // for initialization

    if (i >= array.Length) // for condition
    {
        goto endOfLoop;
    }

startLoop:
    var item = array[i];
    // ...
    goto endOfLoop; // break
    // ...
    ++i;           // for post-expression

    if (i >= array.Length) // for condition
    {
        goto startLoop;
    }

endOfLoop:
    // ...

Como você pode ver, agora temos um ramo inverso, que é nosso pequeno loop. A única coisa que ainda é desagradável aqui é o ramo com o qual acabamos devido à nossa breakdeclaração. Em alguns casos, podemos mudar isso da mesma maneira, mas em outros, existe para ficar.

Então, por que o compilador faz isso? Bem, se conseguirmos desenrolar o loop, poderemos vetorizá-lo. Podemos até provar que há apenas constantes sendo adicionadas, o que significa que todo o nosso loop pode desaparecer no ar. Para resumir: tornando os padrões previsíveis (tornando os ramos previsíveis), podemos provar que certas condições se mantêm em nosso loop, o que significa que podemos fazer mágica durante a otimização do JIT.

No entanto, os ramos tendem a quebrar esses bons padrões previsíveis, o que é algo que otimiza, portanto, um desagrado. Quebre, continue, vá - todos eles pretendem quebrar esses padrões previsíveis - e, portanto, não são realmente "agradáveis".

Você também deve perceber, neste ponto, que um simples foreaché mais previsível do que um monte degoto declarações que se espalham por todo o lugar. Em termos de (1) legibilidade e (2) da perspectiva do otimizador, é a melhor solução.

Outra coisa que vale a pena mencionar é que é muito relevante para a otimização de compiladores atribuir registros a variáveis ​​(um processo chamado alocação de registros ). Como você deve saber, existe apenas um número finito de registros em sua CPU e eles são de longe os pedaços de memória mais rápidos em seu hardware. As variáveis ​​usadas no código que está no loop mais interno têm mais probabilidade de obter um registro atribuído, enquanto as variáveis ​​fora do loop são menos importantes (porque esse código provavelmente é atingido menos).

Ajuda, muita complexidade ... o que devo fazer?

A conclusão é que você deve sempre usar as construções de linguagem que tem à sua disposição, que geralmente (implicitamente) criam padrões previsíveis para o seu compilador. Tente evitar ramos estranhos se possível (especificamente: break, continue, gotoou returnno meio do nada).

A boa notícia aqui é que esses padrões previsíveis são fáceis de ler (para humanos) e fáceis de detectar (para compiladores).

Um desses padrões é chamado SESE, que significa Saída única de entrada única.

E agora chegamos à verdadeira questão.

Imagine que você tem algo parecido com isto:

// a is a variable.

for (int i=0; i<100; ++i) 
{
  for (int j=0; j<100; ++j)
  {
     // ...

     if (i*j > a) 
     {
        // break everything
     }
  }
}

A maneira mais fácil de fazer disso um padrão previsível é simplesmente eliminar ifcompletamente:

int i, j;
for (i=0; i<100 && i*j <= a; ++i) 
{
  for (j=0; j<100 && i*j <= a; ++j)
  {
     // ...
  }
}

Em outros casos, você também pode dividir o método em 2 métodos:

// Outer loop in method 1:

for (i=0; i<100 && processInner(i); ++i) 
{
}

private bool processInner(int i)
{
  int j;
  for (j=0; j<100 && i*j <= a; ++j)
  {
     // ...
  }
  return i*j<=a;
}

Variáveis ​​temporárias? Bom, ruim ou feio?

Você pode até decidir retornar um booleano de dentro do loop (mas eu pessoalmente prefiro o formulário SESE, porque é assim que o compilador o verá e acho que é mais fácil de ler).

Algumas pessoas pensam que é mais limpo usar uma variável temporária e propõem uma solução como esta:

bool more = true;
for (int i=0; i<100; ++i) 
{
  for (int j=0; j<100; ++j) 
  {
     // ...
     if (i*j > a) { more = false; break; } // yuck.
     // ...
  }
  if (!more) { break; } // yuck.
  // ...
}
// ...

Pessoalmente, sou contra essa abordagem. Veja novamente como o código é compilado. Agora pense no que isso fará com esses padrões agradáveis ​​e previsíveis. Obter a foto?

Certo, deixe-me explicar. O que acontecerá é que:

  • O compilador escreverá tudo como ramificações.
  • Como uma etapa de otimização, o compilador fará a análise do fluxo de dados na tentativa de remover a morevariável estranha que só é usada no fluxo de controle.
  • Se for bem-sucedida, a variável more será eliminada do programa e apenas as ramificações permanecerão. Essas ramificações serão otimizadas, para que você obtenha apenas uma ramificação fora do loop interno.
  • Se malsucedida, a variável moreé definitivamente usada no loop mais interno; portanto, se o compilador não a otimizar, há uma grande chance de ser alocado para um registro (que consome uma memória valiosa do registro).

Portanto, para resumir: o otimizador em seu compilador enfrentará muitos problemas para descobrir que moreé usado apenas para o fluxo de controle e, na melhor das hipóteses , o converterá em um único ramo fora do externo para ciclo.

Em outras palavras, o melhor cenário é que ele acabe com o equivalente a isso:

for (int i=0; i<100; ++i) 
{
  for (int j=0; j<100; ++j)
  {
     // ...
     if (i*j > a) { goto exitLoop; } // perhaps add a comment
     // ...
  }
  // ...
}
exitLoop:

// ...

Minha opinião pessoal sobre isso é bastante simples: se é isso que pretendemos o tempo todo, vamos facilitar o mundo para o compilador e a legibilidade, e escreva isso imediatamente.

tl; dr:

Bottom line:

  • Use uma condição simples no seu loop for, se possível. Atenha-se ao máximo das construções de linguagem de alto nível que você tiver à sua disposição.
  • Se tudo falhar e você ficar com um gotoou outro bool more, prefira o primeiro.
atlaste
fonte
OTOH: Eu raramente tenho o desejo de quebrar loops externos ou desejar goto-equiv (e muito código que pode ser escrito de maneira mais limpa), embora possa ser mais comum em outros domínios. Hoje era esse o caso, mas isso foi em grande parte devido a yield return. Ou seja, embora seja fácil mostrar que existem alguns casos úteis, para a maioria dos códigos de "nível de aplicativo" é provavelmente uma ocorrência muito baixa de frustração com C #, excluindo aqueles "apenas provenientes de" C / C ++ ;-) Também ocasionalmente sinto falta O "lançamento" de Ruby (sem exceção), ocasionalmente, também se encaixa nesse domínio.
user2864740
1
@ Suncat2000 A introdução de mais códigos, como variáveis ​​e ramificações condicionais, para evitar o uso de a goto, não torna seu código mais legível - eu diria o que acontece é exatamente o oposto: na verdade, seu fluxo de controle conterá mais nós - o que é praticamente a definição de complexidade. Apenas evitá-lo para ajudar a autodisciplina parece contraproducente em termos de legibilidade.
atlaste 17/03
@ atlaste: Desculpe, deixe-me declarar meu comentário mais claramente. O que eu deveria ter dito foi: Boa explicação. Mas as pessoas que evitam ir ao encontro não têm a ver com desempenho; trata-se de evitar abusos que causam falta de legibilidade. Podemos assumir que as pessoas que evitam gotosimplesmente não têm disciplina para não abusar dela.
Suncat2000 18/03
@ atlaste, que idioma é "aceitar"?
Pacerier 15/04
11

fator em uma função / método e use retorno antecipado ou reorganize seus loops em uma cláusula while. Ir para / exceções / o que quer que certamente não seja apropriado aqui.

def do_until_equal():
  foreach a:
    foreach b:
      if a==b: return
Dustin Getz
fonte
10

Você pediu uma combinação de rápido, agradável, sem uso de um booleano, sem uso de goto e C #. Você descartou todas as formas possíveis de fazer o que deseja.

A maneira mais rápida e menos feia é usar um goto.

Programador do Windows
fonte
Concordo plenamente. Introduzir um novo método apenas para se livrar de um único goto é bobagem. Se o compilador não puder incorporar a chamada desse método por qualquer motivo - acabaremos com a sobrecarga desnecessária de uma chamada de método extra. Lançar e capturar exceções apenas para quebrar o ciclo é ao mesmo tempo mais detalhado e ridiculamente mais caro.
Zar Shardan 06/06/19
@Windowsprogramm: O OP não pediu "sem uso do goto". Ele não queria "ir para outro método". A questão estava longe de excluir todas as formas possíveis, mas você está certo ao sugerir que ir é o melhor aqui.
Suncat2000
5

Às vezes, é bom abstrair o código em sua própria função e depois usar um retorno antecipado - os retornos antecipados são maus:)

public void GetIndexOf(Transform transform, out int outX, out int outY)
{
    outX = -1;
    outY = -1;

    for (int x = 0; x < Columns.Length; x++)
    {
        var column = Columns[x];

        for (int y = 0; y < column.Transforms.Length; y++)
        {
            if(column.Transforms[y] == transform)
            {
                outX = x;
                outY = y;

                return;
            }
        }
    }
}
Céu
fonte
1
mas então isso permite a solução tradicional perguntado por OP
Surya Pratap
2

Eu já vi muitos exemplos que usam "break", mas nenhum que usa "continue".

Ainda seria necessário algum tipo de sinalizador no loop interno:

while( some_condition )
{
    // outer loop stuff
    ...

    bool get_out = false;
    for(...)
    {
        // inner loop stuff
        ...

        get_out = true;
        break;
    }

    if( get_out )
    {
        some_condition=false;
        continue;
    }

    // more out loop stuff
    ...

}
dviljoen
fonte
1

Desde que vi breakC pela primeira vez há algumas décadas, esse problema me incomodou. Eu esperava que algum aprimoramento de idioma tivesse uma extensão para quebrar, que funcionaria assim:

break; // our trusty friend, breaks out of current looping construct.
break 2; // breaks out of the current and it's parent looping construct.
break 3; // breaks out of 3 looping constructs.
break all; // totally decimates any looping constructs in force.
Jesse C. Slicer
fonte
3
Em seguida, um programador de manutenção inserirá outro nível de aninhamento, corrigirá algumas das instruções de interrupção e interromperá outras. A correção para isso é quebrar em um rótulo. Isso realmente foi proposto, mas os pragmáticos usam para ir ao rótulo.
Programador Windows
Espere, quem faz a programação de manutenção mais? :)
Jesse C. Slicer
2
O JavaScript até rotulou instruções de blocos / quebras. devguru.com/Technologies/ecmascript/quickref/break.html
David Grant
@ Chris Bartow: legal! fiz meu Natal :) @David Grant: então parece que o JS quebrou == C vai?
Jesse C. Slicer
D rotulou break / continua
BCS
0

Lembro-me dos meus dias de estudante que foi dito que é matematicamente comprovável que você pode fazer qualquer coisa no código sem um goto (ou seja, não há situação em que goto seja a única resposta). Então, eu nunca uso goto's (apenas minha preferência pessoal, não sugerindo que estou certo ou errado)

De qualquer forma, para sair dos loops aninhados, faço algo assim:

var isDone = false;
for (var x in collectionX) {
    for (var y in collectionY) {
        for (var z in collectionZ) {
            if (conditionMet) {
                // some code
                isDone = true;
            }
            if (isDone)
                break;
        }
        if (isDone) 
            break;
    }
    if (isDone)
        break;
}

... espero que ajude quem gosta de mim é anti-goto "fanboys" :)

MG123
fonte
Lamento dizer, mas o seu professor é o culpado por sua condição. Se ele se preocupou em forçá-lo a aprender montagem, então você sabe que 'ir' é apenas um salto (é claro que estou ignorando o fato de que isso é uma questão # #).
Cmroanirgo 28/12/16
0

Foi assim que eu fiz. Ainda uma solução alternativa.

foreach (var substring in substrings) {
  //To be used to break from 1st loop.
  int breaker=1;
  foreach (char c in substring) {
    if (char.IsLetter(c)) {
      Console.WriteLine(line.IndexOf(c));
      \\setting condition to break from 1st loop.
      breaker=9;
      break;
    }
  }
  if (breaker==9) {
    break;
  }
}

Garvit Arora
fonte
0

A maneira mais fácil de terminar um loop duplo seria terminar diretamente o primeiro loop

string TestStr = "The frog jumped over the hill";
char[] KillChar = {'w', 'l'};

for(int i = 0; i < TestStr.Length; i++)
{
    for(int E = 0; E < KillChar.Length; E++)
    {
        if(KillChar[E] == TestStr[i])
        {
            i = TestStr.Length; //Ends First Loop
            break; //Ends Second Loop
        }
    }
}
Kyle
fonte
Isso não funciona, pois usando break você terminará o loop interno. O loop externo continuará a iterar.
Alex Leo
0

Os loops podem ser interrompidos usando condições personalizadas no loop, permitindo um código limpo.

    static void Main(string[] args)
    {
        bool isBreak = false;
        for (int i = 0; ConditionLoop(isBreak, i, 500); i++)
        {
            Console.WriteLine($"External loop iteration {i}");
            for (int j = 0; ConditionLoop(isBreak, j, 500); j++)
            {
                Console.WriteLine($"Inner loop iteration {j}");

                // This code is only to produce the break.
                if (j > 3)
                {
                    isBreak = true;
                }                  
            }

            Console.WriteLine("The code after the inner loop will be executed when breaks");
        }

        Console.ReadKey();
    }

    private static bool ConditionLoop(bool isBreak, int i, int maxIterations) => i < maxIterations && !isBreak;   

Com esse código, obtemos a seguinte saída:

  • Iteração de loop externo 0
  • Iteração do loop interno 0
  • Iteração de loop interno 1
  • Iteração de loop interno 2
  • Iteração do circuito interno 3
  • Iteração do circuito interno 4
  • O código após o loop interno será executado quando houver interrupções
Daniel Fuentes
fonte
0

Uma outra opção é uma função anônima auto-chamada . Sem ir, sem rótulo, sem nova variável, sem novo nome de função usado e uma linha menor que o exemplo do método anônimo na parte superior.

// self-invoked-anonymous-function
new Action(() =>
{
    for (int x = 0; x < 100; x++)
    {
        for (int y = 0; y < 100; y++)
        {
            return; // exits self invoked lambda expression
        }
    }
})();
Console.WriteLine("Hi");
Robert Einhorn
fonte
-3

Lance uma exceção personalizada que sai do loop externo.

Funciona para for, foreachou whilequalquer tipo de loop e qualquer linguagem que use try catch exceptionblock

try 
{
   foreach (object o in list)
   {
      foreach (object another in otherList)
      {
         // ... some stuff here
         if (condition)
         {
            throw new CustomExcpetion();
         }
      }
   }
}
catch (CustomException)
{
   // log 
}
Moesio
fonte
-4
         bool breakInnerLoop=false
        for(int i=0;i<=10;i++)
        {
          for(int J=0;i<=10;i++)
          {
              if(i<=j)
                {
                    breakInnerLoop=true;
                    break;
                }
          }
            if(breakInnerLoop)
            {
            continue
            }
        }
mounesh Hiremani
fonte
Qual é a diferença essencial com a resposta de dviljoen?
Gert Arnold
Isso não funcionará, porque você não verifica a condição "breakInnerLoop" no loop for externo; portanto, apenas itera para o próximo loop, também escreveu j e J e perdeu um ponto e vírgula, que não será compilado.
28416 Sebastian
-4

Como vejo que você aceitou a resposta na qual a pessoa o encaminha para a declaração goto, onde na programação moderna e na opinião de especialistas goto é um assassino, chamamos isso de assassino na programação, com algumas razões, as quais não discutirei aqui. Neste ponto, mas a solução da sua pergunta é muito simples, você pode usar um sinalizador booleano nesse tipo de cenário, como demonstrarei no meu exemplo:

            for (; j < 10; j++)
            {
                //solution
                bool breakme = false;
                for (int k = 1; k < 10; k++)
                {
                   //place the condition where you want to stop it
                    if ()
                    {
                        breakme = true;
                        break;
                    }
                }

                if(breakme)
                    break;
               }

simples e claro. :)

Steve
fonte
leia a pergunta antes de sugerir uma pausa. Daí os votos negativos.
Cmranirgo
1
leia-o agora, mas quando publiquei esta resposta a versão editada de não usar booleano não foi adicionada, é por isso que publiquei essa resposta .. mas obrigado pelo voto negativo!
Steve
1
Não é uma coisa pessoal. Infelizmente, a votação está agora bloqueada em;) É uma parte necessária de modo a obter as melhores respostas para o topo (o que nem sempre acontece)
cmroanirgo
-6

Você olhou para a breakpalavra - chave? Oo

Este é apenas pseudo-código, mas você deve entender o que quero dizer:

<?php
for(...) {
    while(...) {
        foreach(...) {
            break 3;
        }
    }
}

Se você pensa em breakser uma função break(), então seu parâmetro seria o número de loops para interromper. Como estamos no terceiro loop do código aqui, podemos sair dos três.

Manual: http://php.net/break

Ingwie Phoenix
fonte
13
Não é uma pergunta php.
Zerga
-10

Eu acho que, a menos que você queira fazer a "coisa booleana", a única solução é realmente jogar. O que você obviamente não deveria fazer ..!

Thomas Hansen
fonte