c# template Rompendo um loop aninhado




raw liquid shopify (16)

Bem, goto embora, mas isso é feio e nem sempre é possível. Você também pode colocar os loops em um método (ou um método anon) e usar o return para sair de volta para o 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");

Note 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");

Se eu tiver um loop for aninhado dentro de outro, como posso sair eficientemente de ambos os loops (interno e externo) da maneira mais rápida possível?

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

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

obrigado

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

Eu não sinto que é certo tirar proveito dos novos recursos do .NET (métodos anon) para fazer algo que é bem fundamental.

Por causa disso, tvon (desculpe não pode soletrar nome de usuário completo!) Tem uma boa solução.

Marc: Bom uso de métodos anon, e isso também é ótimo, mas como eu poderia estar em um trabalho onde não usamos uma versão do .NET / C # que suporte métodos anon, eu preciso conhecer uma abordagem tradicional também.


Você viu a palavra break chave break ? Oo

Este é apenas um pseudo-código, mas você deve ser capaz de ver o que quero dizer:

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

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

Manual: http://php.net/break


Use um protetor adequado no loop externo. Coloque o protetor no laço 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 para 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
}

Dependendo da sua situação, você poderá fazer isso, mas somente se o seu código não estiver sendo executado APÓS o loop interno.

for (int i = 0; i < 100; i++)
{
    for (int j = 0; j < 100; j++)
    {
        i = 100;
        break;
    }
}

Não é elegente, mas pode ser a solução mais fácil dependendo do seu problema.


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


Adaptação C # da abordagem geralmente usada no valor de C - set da variável do loop externo fora das condições de loop (por exemplo, para o loop usando a variável int INT_MAX -1 geralmente é 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 nota no código diz que break não vai magicamente pular para a próxima iteração do loop externo - então se você tiver código fora do loop interno, essa abordagem requer mais verificações. Considere outras soluções nesse caso.

Essa abordagem funciona com loops for e while mas não funciona para foreach . No caso do foreach você não terá acesso ao código do enumerador oculto, portanto, você não poderá alterá-lo (e mesmo se você puder, o IEnumerator não possui um método "MoveToEnd").

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


Você pediu uma combinação de rápido, bom, sem uso de um booleano, sem uso de goto e C #. Você descartou todas as maneiras possíveis de fazer o que você quer.

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


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


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

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

Desde que eu vi pela primeira vez em C um par de décadas atrás, esse problema me irritou. Eu esperava que algum aperfeiçoamento de linguagem tivesse uma extensão para quebrar, o 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.

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

De qualquer forma, para sair de loops aninhados eu 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 para quem gosta de mim é anti-goto "fanboys" :)


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

Ele ainda exigiria um 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
    ...

}

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

Lançar uma exceção personalizada que sai loop outter.

Ele funciona para, foreach ou while ou qualquer tipo de loop e qualquer linguagem que usa try catch exception block try catch exception

try 
{
   foreach (object o in list)
   {
      foreach (object another in otherList)
      {
         // ... some stuff here
         if (condition)
         {
            throw new CustomExcpetion();
         }
      }
   }
}
catch (CustomException)
{
   // log 
}

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

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

outer: while(fn1())
{
   while(fn2())
   {
     if(fn3()) continue outer;
     if(fn4()) break outer;
   }
}

Não me cite sobre isso, mas você poderia usar o goto conforme sugerido no MSDN. Existem outras soluções, como incluir um sinalizador que é verificado em cada iteração de ambos os loops. Finalmente, você poderia 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: ;

Condição:

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




nested-loops