web-dev-qa-db-fra.com

Sortir d'une boucle imbriquée

Si j'ai une boucle for imbriquée dans une autre, comment puis-je sortir efficacement des deux boucles (interne et externe) le plus rapidement possible?

Je ne veux pas être obligé d'utiliser un booléen et de devoir ensuite passer à une autre méthode, mais plutôt d'exécuter la première ligne de code après la boucle externe.

Quel est un moyen rapide et agréable de s'y prendre?

Merci


Je pensais que les exceptions ne sont pas bon marché/ne devraient être lancées que dans un état vraiment exceptionnel, etc. Par conséquent, je ne pense pas que cette solution serait bonne du point de vue des performances.

Je ne pense pas qu'il soit juste de tirer parti des nouvelles fonctionnalités de .NET (méthodes anon) pour faire quelque chose d'assez fondamental.

Pour cette raison, tvon (désolé, je ne peux pas épeler le nom d'utilisateur complet!) A une solution intéressante.

Marc: C'est une bonne utilisation des méthodes anon, et c'est très bien aussi, mais comme je pourrais occuper un poste où nous n'utilisons pas une version de .NET/C # prenant en charge les méthodes anon, j'ai également besoin d'une approche traditionnelle.

189
dotnetdev

goto, mais c’est moche et pas toujours possible. Vous pouvez également placer les boucles dans une méthode (ou une méthode anonyme) et utiliser return pour revenir au code principal.

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

contre:

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

Notez qu'en C # 7, nous devrions obtenir des "fonctions locales", ce qui (syntaxe à déterminer, etc.) signifie que cela devrait fonctionner de la manière suivante:

// 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");
184
Marc Gravell

Adaptation C # de l'approche souvent utilisée dans C - définir la valeur de la variable de la boucle extérieure en dehors des conditions de boucle (c'est-à-dire que la boucle utilisant la variable int INT_MAX -1 est souvent un bon choix):

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    
}

Comme le note le code, break ne passera pas comme par magie à la prochaine itération de la boucle externe. Par conséquent, si vous avez du code en dehors de la boucle interne, cette approche nécessite davantage de vérifications. Envisagez d’autres solutions dans ce cas.

Cette approche fonctionne avec les boucles for et while mais ne fonctionne pas pour foreach. Dans le cas de foreach, vous n’avez pas accès au code de l’énumérateur masqué, vous ne pouvez donc pas le modifier (et même si vous pouviez IEnumerator ne pas utiliser de méthode "MoveToEnd").

Remerciements aux auteurs des commentaires en ligne:
i = INT_MAX - 1 suggestion de Meta
for/foreach commentaire de ygoe .
Propre IntMax par jmbpiano
remarque sur le code après la boucle intérieure de blizpasta

93
Nils Pipenbrinck

Cette solution ne s'applique pas à C #

Pour les personnes qui ont trouvé cette question via d'autres langues, Javascript, Java et D autorise les pauses libellées et continue :

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

Utilisez une protection appropriée dans la boucle extérieure. Placez la garde dans la boucle intérieure avant de vous casser.

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, mieux encore, résumez la boucle interne dans une méthode et quittez la boucle externe lorsqu'elle renvoie false.

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

    .... some outer loop stuff

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

    ... more outer loop stuff
}
25
tvanfosson

Ne me citez pas à ce sujet, mais vous pouvez utiliser goto comme suggéré dans le MSDN. Il existe d'autres solutions, notamment un indicateur coché à chaque itération des deux boucles. Enfin, vous pourriez utiliser une exception comme une solution vraiment lourde à votre problème.

ALLER À:

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

État:

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

Exception:

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

Est-il possible de refactoriser la boucle for imbriquée dans une méthode privée? De cette façon, vous pouvez simplement «sortir» de la méthode pour sortir de la boucle.

14
NoizWaves

facteur dans une fonction/méthode et utilisez le retour anticipé, ou réorganisez vos boucles dans une clause while. goto/exceptions/tout ce qui ne convient certainement pas ici.

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

Vous avez demandé une combinaison de quick, Nice, pas d'utilisation d'un booléen, pas d'utilisation de goto et de C #. Vous avez exclu tous les moyens possibles de faire ce que vous voulez.

Le moyen le plus rapide et le moins moche est d'utiliser un goto.

8
Windows programmer

Parfois, il est agréable d’abréger le code dans sa propre fonction et d’utiliser un retour rapide - les premiers retours sont pervers:)

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

En fonction de votre situation, vous pourrez peut-être faire cela, mais seulement si votre code n'est pas exécuté APRÈS la boucle interne.

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

Ce n'est pas élégant, mais c'est peut-être la solution la plus simple en fonction de votre problème.

2
Chris Bartow

J'ai vu beaucoup d'exemples qui utilisent "break" mais aucun qui utilise "continue".

Il faudrait quand même un drapeau dans la boucle intérieure:

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

}
2
dviljoen

Depuis que j’ai vu pour la première fois break en C il y a deux décennies, ce problème me vexait. J'espérais qu'une amélioration de la langue aurait une extension à briser qui fonctionnerait ainsi:

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.
1
Jesse C. Slicer

Je me souviens de mes années d’étudiant qu’il était dit qu’il était mathématiquement prouvable que vous pouviez tout faire dans le code sans goto (c’est-à-dire qu’il n’y avait pas de situation où goto était la seule réponse). Donc, je n'utilise jamais de goto (juste ma préférence personnelle, ne suggérant pas que j'ai raison ou tort)

Quoi qu'il en soit, pour sortir des boucles imbriquées, je fais quelque chose comme ceci:

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

... j'espère que cela aide pour ceux qui, comme moi, sont des "fanboys" anti-goto :)

0
MG123

C'est comme ça que je l'ai fait. Encore une solution de contournement.

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

0
Garvit Arora