Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is goto ok for breaking out of nested loops?

Tags:

javascript

c#

JavaScript supports a goto like syntax for breaking out of nested loops. It's not a great idea in general, but it's considered acceptable practice. C# does not directly support the break labelName syntax...but it does support the infamous goto.

I believe the equivalent can be achieved in C#:

    int i = 0;            
    while(i <= 10)
    {
        Debug.WriteLine(i);
        i++;
        for(int j = 0; j < 3; j++)
            if (i > 5)
            {
                goto Break;//break out of all loops
            }
    }

    Break:

By the same logic of JavaScript, is nested loop scenario an acceptable usage of goto? Otherwise, the only way I am aware to achieve this functionality is by setting a bool with appropriate scope.

like image 596
P.Brian.Mackey Avatar asked Oct 24 '11 21:10

P.Brian.Mackey


People also ask

Does goto break loop?

@MatheusRocha Yes, it does. In fact, C++ has special rules about goto that prevent programmers from making improper jumps, e.g. jumping over initialization.

How do you break out of one nested loop?

Using break in a nested loop In a nested loop, a break statement only stops the loop it is placed in. Therefore, if a break is placed in the inner loop, the outer loop still continues. However, if the break is placed in the outer loop, all of the looping stops.

Is nesting of goto statement possible?

This article demonstrates that local goto statements create RAII for C code. The code is neat an easy to follow. Imagine that as a series of nested if statements. I understand that goto is taboo in many other languages because their exists other control mechanisms like try/catch etc, however, in C it seems appropriate.

Does continue break out of nested for loop?

Break out of nested loops with else and continue You can break all loops with else and continue . The code with explanation is as follows. When the inner loop ends normally without break , continue in the else clause is executed.


6 Answers

My opinion: complex code flows with nested loops are hard to reason about; branching around, whether it is with goto or break, just makes it harder. Rather than writing the goto, I would first think really hard about whether there is a way to eliminate the nested loops.

A couple of useful techniques:

First technique: Refactor the inner loop to a method. Have the method return whether or not to break out of the outer loop. So:

for(outer blah blah blah)
{
    for(inner blah blah blah)
    {
        if (whatever)
        {
             goto leaveloop;      
        }
    }
}
leaveloop:    
...

becomes

for(outer blah blah blah)
{
    if (Inner(blah blah blah))
        break;
}

...

bool Inner(blah blah blah)
{
    for(inner blah blah blah)
    {
        if (whatever)
        {
             return true;      
        }
    }
    return false;
}

Second technique: if the loops do not have side effects, use LINQ.

// fulfill the first unfulfilled order over $100
foreach(var customer in customers)
{
    foreach(var order in customer.Orders)
    {
        if (!order.Filled && order.Total >= 100.00m)
        {
             Fill(order);
             goto leaveloop;      
        }
    }
}
leaveloop:    

instead, write:

var orders = from customer in customers
             from order in customer.Orders;
             where !order.Filled
             where order.Total >= 100.00m
             select order;
var orderToFill = orders.FirstOrDefault();
if (orderToFill != null) Fill(orderToFill);

No loops, so no breaking out required.

Alternatively, as configurator points out in a comment, you could write the code in this form:

var orderToFill = customers
    .SelectMany(customer=>customer.Orders)
    .Where(order=>!order.Filled)
    .Where(order=>order.Total >= 100.00m)
    .FirstOrDefault();
if (orderToFill != null) Fill(orderToFill);

The moral of the story: loops emphasize control flow at the expense of business logic. Rather than trying to pile more and more complex control flow on top of each other, try refactoring the code so that the business logic is clear.

like image 157
Eric Lippert Avatar answered Sep 27 '22 21:09

Eric Lippert


I would personally try to avoid using goto here by simply putting the loop into a different method - while you can't easily break out of a particular level of loop, you can easily return from a method at any point.

In my experience this approach has usually led to simpler and more readable code with shorter methods (doing one particular job) in general.

like image 39
Jon Skeet Avatar answered Sep 26 '22 21:09

Jon Skeet


Let's get one thing straight: there is nothing fundamentally wrong with using the goto statement, it isn't evil - it is just one more tool in the toolbox. It is how you use it that really matters, and it is easily misused.

Breaking out of a nested loop of some description can be a valid use of the statement, although you should first look to see if it can be redesigned. Can your loop exit expressions be rewritten? Are you using the appropriate type of loop? Can you filter the list of data you may be iterating over so that you don't need to exit early? Should you refactor some loop code into a separate function?

like image 32
slugster Avatar answered Sep 29 '22 21:09

slugster


IMO it is acceptable in languages that do not support break n; where n specifies the number of loops it should break out.
At least it's much more readable than setting a variable that is then checked in the outer loop.

like image 21
ThiefMaster Avatar answered Sep 26 '22 21:09

ThiefMaster


I believe the 'goto' is acceptable in this situation. C# does not support any nifty ways to break out of nested loops unfortunately.

like image 20
tier1 Avatar answered Sep 29 '22 21:09

tier1


It's a bit of a unacceptable practice in C#. If there's no way your design can avoid it, well, gotta use it. But do exhaust all other alternatives first. It will make for better readability and maintainability. For your example, I've crafted one such potential refactoring:

void Original()
{
    int i = 0;            
    while(i <= 10)
    {
        Debug.WriteLine(i);
        i++;
        if (Process(i))
        {
            break;
        }
    }
}

bool Process(int i)
{
    for(int j = 0; j < 3; j++)
        if (i > 5)
        {
            return true;
        }
    return false;
}
like image 23
Jesse C. Slicer Avatar answered Sep 27 '22 21:09

Jesse C. Slicer