25

Possible Duplicate:
Breaking out of a nested loop

I have this code

foreach (___)
{
    foreach (___)
    {
        foreach (___)
        {
            if (condition)
            {
                //break out of all loops
            }
        }
    }
}

But break only "breaks" the most inner loop (sorry for my english) and I want to leave them all...

I was thinking about :

  1. GOTO, but I've always been told to not use it
  2. A sort of boolean-flag

Is there any more elegant way ? like provided by C# ?

Thanks in advance for any help !

Community
  • 1
  • 1
  • Clever, but it will only break out of it's own for loop after the first iteration. – Robert Rouhani Jan 17 '12 at 08:45
  • @RobertRouhani a joke. Obviously not funny at all. – sshow Jan 17 '12 at 08:46
  • I think same question is answered in link http://stackoverflow.com/questions/324831/breaking-out-of-a-nested-loop – Manjunath K Mayya Jan 17 '12 at 08:52
  • @JérémyTalio: unrelated to your question, it is also possible that this code can be somehow refactored to avoid nested loops, but it's impossible to say without actual code inside those loops. – vgru Jan 17 '12 at 09:31
  • -1 for the FUD against `goto` – o0'. Jan 17 '12 at 10:59
  • 3
    I never ever had that many nested loops in a function nor need to break out of that many. Perhaps rewriting the code the solution. Also learn to use the Contains function (from linq). –  Jan 17 '12 at 11:55

9 Answers9

55

A very elegant solution to this is to move the entire nest of loops to a separate method and return; when you want to break out of all loops.

Robert Rouhani
  • 14,512
  • 6
  • 44
  • 59
54

A goto is perfectly fine here:

foreach (___)
{
    foreach (___)
    {
        if (condition)
        {
            goto done;
        }
    }
}
done: ;
dtb
  • 213,145
  • 36
  • 401
  • 431
  • 43
    +1 for not being afraid! :) – leppie Jan 17 '12 at 08:42
  • 18
    See [Cargo Cult Programming](http://en.wikipedia.org/wiki/Cargo_cult_programming). I claim that peole downvoting this don't really understand *why* `goto` is considered harmful and just avoid it because they have heard the `goto is evil` mantra often enough (which is true *in principle*, but only *in principle*). – Heinzi Jan 17 '12 at 08:46
  • 4
    Other than refactoring to use a method, I haven't seen a cleaner and more readable solution than a `goto` to solve this. – John N Jan 17 '12 at 09:06
  • And i claim, Heinzi m, tha you blindly follow a stupid book and dont see a clear example when there is one. There are few cases where Goto is a good approach, this is one. Don't like it - livei n your world. – TomTom Jan 17 '12 at 09:56
  • 1
    @TomTom: I *do* see this as a valid use of `goto` (and I also upvoted your answer as well as this one, btw.). Maybe you misunderstood my comment? – Heinzi Jan 17 '12 at 09:59
  • @Heinzi There’s a big difference between cargo cult and sticking to guidelines. In a way, your application of “cargo cult” here is cargo cult. That said, I actually find using `goto` here somewhat excusable. I *still* find refactoring into a separate method and using `return` a *better* solution. – Konrad Rudolph Jan 17 '12 at 10:18
  • 6
    @Heinzi, using `goto` may be fine here, but you will eventually waste a lot of your time arguing about it with your colleagues. It's better to refactor this into a separate function. – avakar Jan 17 '12 at 10:19
  • 1
    `goto` should be taken out of C/C++/C# and **shot**... – Jay Jan 17 '12 at 10:20
  • 2
    @avakar: if your collegues are stupid, educate them or ignore them. Using a wrong solution to please their ignorance is definitely *not* the correct thing to do. – o0'. Jan 17 '12 at 11:01
  • 1
    @Heinzi what do you want to bet the down-voter at some point this month quotes Knuth on "premature optimisation"? (Which is a quote from a paper in which he defended the use of goto in certain cases). – Jon Hanna Jan 17 '12 at 11:46
  • @avakar: I agree that peer pressure alone is not the right reason for refactoring. But the purpose of code review is to discuss the code and agree on a best approach. I doubt that you will have several senior developers dismissing a type of coding based solely on religious reasons. Refactoring a long method into several shorter ones is in most cases welcome, so in this case I would agree that: a) `goto` is acceptable, but b) it may be refactored further. A goto label like this allows you to jump there from **any place in that method**, and that's pure spaghetti potential. – vgru Jan 17 '12 at 12:03
  • Does a mod have to lock/delete comments? Civility! Also, `goto`? Yeeesh. I think if you find a good use for it then you've found some code you should refactor. –  Jan 17 '12 at 12:14
  • @Groo: I agree this code *likely* should be refactored anyway; I guess no one said anything about that so far because it was obvious to everyone ^^ – o0'. Jan 17 '12 at 12:45
6

The best way is probably to refactor to smaller methods with return statements. But if you need a lot of variables in scope you can always use a delegate.

   Action work = delegate
        {
            foreach (___)
            {
                 foreach (___)
                 {
                       foreach (___)
                       {
                            if (condition)
                            {
                                 return;
                            }
                       }
                  }
             }

        };

  work();
Glenn
  • 1,955
  • 2
  • 15
  • 23
  • 9
    That's a really nasty way to circumvent `goto`. :-D It takes more time to realize that the `return` only exits the anonymous method. – vgru Jan 17 '12 at 09:11
  • @Groo For sure, this is only a edge case solution to Robert Rouhani´s own comment on his accepted answer. – Glenn Jan 17 '12 at 09:15
  • For extra sneakiness, I would omit the variable and simply invoke the delegate after declaration: `delegate { ... }();` :-D Only kidding, it's perfectly valid as any other example here. I would still probably extract it to a method, although three nested `foreach` loops can most likely be refactored using LINQ or something else. – vgru Jan 17 '12 at 09:28
  • @Groo why nasty? Having an explicit label makes it possible to jump to it from some other place (I was told); here the explicit label is eliminated, so, *nice*, not? And I kinda like your second suggestion too, eliminating another explicit one-use name. BTW just as aside, Common Lisp has named scopes and return-from can escape from any named scope, not only that of a method (function). – Will Ness Jan 17 '12 at 10:00
5

Years ago, when I was in school, we had informatics with a pretty out of the books stupid teacher. One rule was "no goto". I had an algorithm with multiple loops that grew to many times the size without goto.

There are good reasons, and breaking out of multiple loops is one.

user
  • 6,897
  • 8
  • 43
  • 79
TomTom
  • 61,059
  • 10
  • 88
  • 148
  • Stupid pupils listening to stupid teachers are the reason we are stuck with these so-called "programmers" which are religiously afraid of using the correct tool for the job. I despise them very much. – o0'. Jan 17 '12 at 11:08
3

Maybe this is the only valid reason to use goto.

Marco
  • 56,740
  • 14
  • 129
  • 152
  • 1
    There may be many valid reasons, this is the most widespread one. – o0'. Jan 17 '12 at 11:03
  • @Lohoris: how about you add an actual answer with your opinion on that matter, show a couple of those valid reasons, and get some peer review from the community? So far, all you've done is called other people stupid because they don't share your opinion. I wouldn't want a coder on my team with such absolute attitude, no matter what side of this *religious* debate you represented. – vgru Jan 17 '12 at 12:34
  • @Groo: why should I add an answer when there already is a perfectly valid one? The goal of this site is not to boost enlarge your e-penis, it is to give answers to people who need them. – o0'. Jan 17 '12 at 12:41
  • goto is usually acceptable in other cases where you need forms of control flow that are not directly or conveniently supported in your language. Common examples include exception handling (in languages without exceptions built in), state machines and coroutines. – hugomg Jan 17 '12 at 13:02
  • @missingno: here we're talking about C#, so my answer depends from this. I really don't understand downvotes here... – Marco Jan 17 '12 at 13:06
  • I mentioned some things in addition to exceptions. I just thought the "maybe" was not clear and useful. – hugomg Jan 17 '12 at 13:10
2
bool bLoopBreak = false;

foreach (___)
{
    foreach (___)
    {
        foreach (___)
        {
            if (condition)
            {
                bLoopBreak = true;
                break;
                //break out of all loops
            }
        }
        if (bLoopBreak) break;
    }
    if (bLoopBreak) break;
}
Shai
  • 25,159
  • 9
  • 44
  • 67
  • +1, because it's the nicest example for the Boolean flag solution in this thread, but note that the question asked for "a more elegant way than a Boolean flag", so, technically, this is not an answer. – Heinzi Jan 17 '12 at 09:01
  • not really maintainable code.. go for the function+return solution instead. – Jay Jan 17 '12 at 10:21
  • 2
    If I ever meet a horror like this one, I'd just scrap the file and do it over again. – o0'. Jan 17 '12 at 11:02
  • 1
    @Lohoris you can scrap whatever u'd like, I'm posting here not because I'm getting paid for, I'm posting here because people *MIGHT* find my answers useful. If it doesn't suit you, you're more than welcome to ignore it. – Shai Jan 17 '12 at 11:24
  • 2
    I spare the downvote here but I hate to see code that is more complicated, error prone and slower just to avoid a perfectly fine language construct only because of its bad reputation. – x4u Jan 17 '12 at 11:29
  • @Shai: and I downvote it because I hope no one ever follows this horrible advice (nothing personal). And I explain why so people who want to understand, can. – o0'. Jan 17 '12 at 12:39
  • @Lohoris: I see no explaination - only a grunt. You might want to revise your comment and explain *why* it's so horrible – Shai Jan 17 '12 at 13:24
  • @Shai: because **Beautiful is better than ugly** – o0'. Jan 31 '12 at 15:03
2

Don't be afraid to use goto in a case like this. break and continue are just syntactic sugar for goto, and is sparingly and commented correctly it can make code clearer than lots of nested checks to see if you should stop looping.

Sean
  • 60,939
  • 11
  • 97
  • 136
  • 1
    You’re dead wrong about “syntactic sugar”. They are *semantically* checked, scoped and compiler ensured “sugar”. Just as any `if` and `for` loop also contains a “goto”. This is *no reason at all* to defend `goto` in code. – Konrad Rudolph Jan 17 '12 at 10:22
  • @KonradRudolph `goto` is semantically checked, scoped, and compiler ensured. They're quite unlike the forms in some languages, and much of Dijkstra's complaints about `goto` is not possible in C#. Indeed, it wouldn't match the "present form" he attacked. – Jon Hanna Jan 17 '12 at 11:50
  • @Jon True but not even remotely to the same extent as `break` or `continue`, both of which are strictly tied to a scoping construct and serve one purpose only. – Konrad Rudolph Jan 17 '12 at 11:53
  • @KonradRudolph `goto` is just as strictly tied to a scoping construct, albeit a larger one. Can you think of a way to do what `goto` does in the method at line 575 of https://github.com/hackcraft/Ariadne/blob/master/Collections/ThreadSafeDictionary.cs with the same performance? I wouldn't use that often, but there are times when squeezing every bit out makes sense. – Jon Hanna Jan 17 '12 at 12:01
  • @Jon I never said (in this thread) never to use `goto`. In particular, manual TCO is a good reason to use it, as you have obviously realised yourself. Still, this is a very low-level reason and I’d argue that the *only* good reasons ever to use `goto` are low-level. – Konrad Rudolph Jan 17 '12 at 12:24
  • @Jon I’m just really annoyed by people claiming that “construct X is just syntactic sugar for `goto`” since it’s such a fundamentally flawed argument. – Konrad Rudolph Jan 17 '12 at 12:26
  • @KonradRudolph I get you, though they have a point. I'd agree with "semantic sugar", but disagree with "just" - the added restrictions on that sugar means it brings more than sweetness. – Jon Hanna Jan 17 '12 at 12:35
-3

PHP lets you do this:

foreach (___)
{
    foreach (___)
    {
        foreach (___)
        {
            break 3; // get out of all 3 loops
        }
    }
}

I don't know a whole lot about C# but it may have a similar feature.

Matt
  • 9,068
  • 12
  • 64
  • 84
-3

Just to throw an alternative in there: You could also throw an exception inside the loop. In C#/Java/Python this is reasonably cheap to do. (In C++, not so much.)

Note that Python has a StopIteration exception for a similar case, so it's not unheard of, although it might be a bit unorthodox in C#-land. ;)

Sometimes, you get the exception for free, and thus it can be used, but you'd better document it if you are taking that approach.

try {
    foreach (___) {
        foreach (___) {
            if(your condition true)  {
                throw new IterationDone(); // or use a singleton to avoid allocating
            }
        }
    }
    // not found
catch (IterationDone e) {
    // yay    
}

I've made the answer a community wiki, so that it can be seen and discussed. (I don't advocate it as the first and best solution, it's just A solution, and thus deserves to be here...)

Macke
  • 24,812
  • 7
  • 82
  • 118
  • 5
    oh the horrible extremes people will do to avoid using goto... No matter how "cheap" it is, it is just semantically wrong. An exception should only be thrown when an error has occurred. – CashCow Jan 17 '12 at 10:51
  • While it is foolish to do that to avoid a `goto`, it is actually the only barely elegant solution I've encountered so far. If I need a `goto` *and the stupid language doesn't have it*, I'd do it this way. – o0'. Jan 17 '12 at 11:05
  • 1
    Actually throwing an exception is a few orders of magnitude slower in C# and Java than using goto in C# or a labled break in Java. – x4u Jan 17 '12 at 11:32
  • @x4u: sure, I was considering only how good the code looked, here. And, barring `goto` (which **just is** the correct answer), this is the only one that looks remotely readable (that is, without giving you the urge to punch the coder in the face). – o0'. Jan 17 '12 at 12:47
  • @CashCow: First, It's not used for errors in all cases, as I said. Second, sometimes the orders of magnitude aren't important compared to the real work, and if you get the exception for free it might not be too bad. Finally, I was just trying to mention a solution that no-one else has given. Sometimes people go to horrible extremes to avoid exceptions too... ;) – Macke Jan 17 '12 at 17:14
  • @Macke I didn't downvote you, and in some ways I saw this coming. I didn't upvote it either because I wouldn't approve of doing this in practical code and it wouldn't get past me in a code review. – CashCow Jan 17 '12 at 18:04
  • @CashCow: Ok, I didn't expect to get (m)any upvotes for it anyway. – Macke Jan 17 '12 at 18:54