14

What is the best way to terminate all nested loops in the example below. Once the if statement is true, I want to terminate the outer for statement (with I). In the other words I need the whole loop to stop. Is there better way than setting I to 10?

for (int I = 0; I < 10; I++)
{
    for (int A = 0; A < 10; A++)
    {
        for (int B = 0; B < 10; B++)
        {
            if (something)
                break;
        }
    }
}
cspolton
  • 4,495
  • 4
  • 26
  • 34
Mirial
  • 247
  • 3
  • 8

14 Answers14

32

I would refactor this to a method, and just call return whenever I need to.

You could also use goto, and I have used goto for this, but it gets frowned upon. Which is dumb; this scenario is why it exists in the language.

void DoSomeStuff()
{
    for (int I = 0; I < 10; I++)
    {
        for (int A = 0; A < 10; A++)
        {
            for (int B = 0; B < 10; B++)
            {
                if (something)
                    return;
            }
        }
    }
}
...somewhere else...
DoSomeStuff();
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • @Marc Gravell:Thanks Mark! This code is actually part of greater method so I cannot call return. But the goto is great idea! – Mirial Jun 01 '11 at 12:24
  • 7
    @Mirial - select the block you want; in VS, "extract method" - this is what I mean by refactor. NOW you have a method you can `return` from. Basically, you *can* refactor your code to do this, and you probably should (it sounds like your method is over-long). – Marc Gravell Jun 01 '11 at 12:25
  • 1
    I think that if this key word would not exists in C#, then young developer would think twice before implement something and create i properly. IMHO the goto instruction should be used only by the compiler. – Damian Leszczyński - Vash Jun 01 '11 at 12:25
  • 3
    @Mirial Refactoring the code to a method is a better way than using goto. – cspolton Jun 01 '11 at 12:27
  • "Marc Gravell facts" are approaching :) – Bastardo Jun 01 '11 at 18:22
15

Don't shoot me, but this might actually warrant a goto:

 for (int I = 0; I < 10; I++) {
      for (int A = 0; A < 10; A++) {
           for (int B = 0; B < 10; B++) {
               if (something)
                   goto endOfTheLine;
            }
      }
  }
  endOfTheLine:
  Console.WriteLine("Pure evilness executed");
chrisaut
  • 1,076
  • 6
  • 17
  • 4
    This is actually a legitimate use of `goto` IMO. – Nobody Jun 01 '11 at 15:35
  • 5
    This is exactly the situation where a goto is the best solution. It doesn't incur the runtime overhead of redundant condition checks and is more to the point and easier to understand than all the alternatives posted so far. The worst downside of goto nowadays is its bad reputation. – x4u Jun 01 '11 at 18:07
  • 1
    I refuse to listen! Shoot him! – VitalyB Jun 02 '11 at 09:30
14

Assuming you want to exit from all loops, you can refactor it into something a little more structured:

bool done = false;
for (int i = 0; i < 10 && !done; i++) {
    for (int a = 0; a < 10 && !done; a++) {
        for (int b = 0; b < 10 && !done; b++) {
            if (something) {
                done = true;
                continue;
            }
        }
    }
}
paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
10

If the loop bodies do not produce a side effect but rather merely are looking for the first value where "something" is true then would fix the problem by eliminating all the loops in the first place.

var query = from I in Enumerable.Range(0, 10)
            from A in Enumerable.Range(0, 10)
            from B in Enumerable.Range(0, 10)
            where something(I, A, B)
            select new { I, A, B };
var result = query.FirstOrDefault();
if (result == null)
{
   Console.WriteLine("no result");
}
else
{
    Console.WriteLine("The first result matching the predicate was {0} {1} {2},
        result.I, result.A, result.B);
}

But don't do this if the loops have side effects; queries are a really bad place to put side effects. If the inner loop has a side effect then you can do something like this:

var triples = from I in Enumerable.Range(0, 10)
              from A in Enumerable.Range(0, 10)
              from B in Enumerable.Range(0, 10)
              select new { I, A, B };
foreach(var triple in triples)
{
    if (something(triple.I, triple.A, triple.B))
        break;
    DoSomeSideEffect(triple.I, triple.A, triple.B);
}

and now there is only one loop to break out of, not three.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
3

Why not do:

 for (int I = 0; I < 10 || !something; I++)
        {
            for (int A = 0; A < 10 || !something; A++)
            {
                for (int B = 0; B < 10; B++)
                {
                    if (something)
                    {
                       I=10;
                       break;
                    }
                }
            }
        }
Yuck
  • 49,664
  • 13
  • 105
  • 135
  • 1
    I think you mean `&& !something` instead of `|| something`. Additionally, something can very well be a method, you want to execute as seldom as possible. – Daniel Hilgarth Jun 01 '11 at 12:27
  • because you would not completely break out. You would fall back into the A-loop and so maybe re-insert into the B-loop. And so you example is simply showing that this isn't a good idea cause it just to hard to read and maintain. – Oliver Jun 01 '11 at 12:28
  • @Oliver: Totally agree, this would be very hard to maintain and it's quite ugly. Refactoring to a method is the best way to go, IMHO, as Marc suggested above. – Yuck Jun 01 '11 at 12:30
  • I like this :) --- probably want `&& !something` though. I don't think I = 10 is necessary – Eben Roux Jun 01 '11 at 12:30
  • 5
    gymnastics to avoid a goto is worse than just using the goto. This is a valid use for a goto... people confuse the issue of "goto considered harmful" and think it's *always* harmful... if used in very specific and limited situations, after knowing and considering all the other options, it's fine – JoelFan Jun 01 '11 at 14:58
3

You could always exploit the fact that there is a conditional statement in the for thus:

bool working = true;
for (int i=0; i<10 && working; i++) 
{
    for (int j=0; j<10 && working; j++) 
    {
        for (int k=0; k<10 && working; k++) 
        {
            Console.WriteLine(String.Format("i={0}, j={1}, k={2}", i,j,k));
            if (i==5 && j==5 && k==5) 
            {
                working = false;
            }
        }
    }
}
Lazarus
  • 41,906
  • 4
  • 43
  • 54
2

I would lean in favour of goto also else you are going to have to exit each loop:

    for (int I = 0; I < 10; I++)
    {
        for (int A = 0; A < 10; A++)
        {
            for (int B = 0; B < 10; B++)
            {
                if (something)
                    break;
            }
            if (something)
                break;
        }
        if (something)
            break;
    }
Eben Roux
  • 12,983
  • 2
  • 27
  • 48
2

If this is the final task in method then you can return when condition is true. otherwise you have to make all the values to max values

if (something)              
    {
        I=10;   
        B=10;
        A=10;
        break;
    }
Umesh CHILAKA
  • 1,466
  • 14
  • 25
2
for (int I = 0; I < 10; I++) {     
     for (int A = 0; A < 10; A++)     {         
         for (int B = 0; B < 10; B++)         {             
            if (something){                 
                  B=13;
                  A=13;
                  I=13;
             }
          }     
     } 
 } 

A very primitive solution.

Bastardo
  • 4,144
  • 9
  • 41
  • 60
2

simple solution is to refactor the nested loops into a separate method with the relevant return type being whatever you wanted to know at that point:

in my case I will assume you wanted the the values of I, A and B at that point, trivial with a Tuple instead.

// original method
...
var x = FindFirst()
...

// separate method
public Tuple<int,int,int> FindFirst()
{
    for (int I = 0; I < 10; I++)
    {
        for (int A = 0; A < 10; A++)
        {
            for (int B = 0; B < 10; B++)
            {
                if (something)
                    return Tuple.Create(I,A,B);
            }
        }    
    }
    return null;
}

If you need to pass in any additional state to the method (for the bounds, or the something bit) just pass them as parameters.

If you wanted to handle failing to find the first one in a different fashion then something like

bool TryFindFirst(out Tuple<int,int,int> x) 

would be an alternate.

As a side note using capital letters for variable names (especially single letter ones) is considered poor style in c# (and many other languages)

ShuggyCoUk
  • 36,004
  • 6
  • 77
  • 101
2

I don't know if C# supports it but some languages support:

break n;

Where n is the number of nested loops to break.

JD Isaacks
  • 56,088
  • 93
  • 276
  • 422
1

You could always meet the loops expectations:

if (something) B = 10

Edit: (Appears you included this in your post through an edit)

If you don't like the way it looks, you could wrap a function such as:

Satisfy(B,10)

Then it looks cleaner, but really not needed.

Serodis
  • 2,092
  • 4
  • 25
  • 34
1

the other possibility is to cascade the check on isSomething in all for loops. you add the

if (something)                         
   break; 

in all 3 loops

fixagon
  • 5,506
  • 22
  • 26
1

Personally I would go with Paxdiablo's method above (+1 for that), but an alternative is below - it depends if the OP needs to know what the I, A and B numbers are when "something" is true, be because iab were declared in the loop I'm guessing not.

bool done = false;
int i, a, b;
for (i = 0; i < 10 ; i++) {
    for (a = 0; a < 10 ; a++) {
        for (b = 0; b < 10 ; b++) {
            if (something) {
                done = true;
                break;
            }
        }
        if (done) break;
    }
    if (done) break;
}
// i, a and B are set to the last numbers where "something" was true
Harag
  • 1,532
  • 4
  • 19
  • 31