Is there a smart way to break out of nested loops?

(Right now I'm mostly using C #. Ideas in other languages ​​are welcome, but please translate them to C # if you can, and explicitly.)

Something I run into over and over again is a nested loop that searches through some kind of 2D array to find an element (usually some object) that then needs to be manipulated. So, of course, once you spot this object, you must break out of both loops so you don't keep looking for what you've already found (especially in nested loops that can traverse exponentially huge arrays).

The following code is currently my preferred way:

Obj O = null;
bool KeepLooping = true;

for (int j = 0; j < height && KeepLooping; j++)
{
    for (int i = 0; i < width; i++)
    {
        if (ObjArray[i, j] != null && ObjArray[i, j].property == search_value)
        {
            O = ObjArray[i, j]; // you found it, now remember it
            KeepLooping = false; // clear the flag so the outer loop will break too
            break;
        }
    }
}

      

Thanks to Eric Funkenbusch, it becomes much more elegant if we do this:

Obj O = null;
for (int j = 0; j < height && O == null; j++) // much, much better idea to check O for null in the outer loop
{
    for (int i = 0; i < width; i++)
    {
        if (ObjArray[i, j] != null && ObjArray[i, j].property == search_value)
        {
            O = ObjArray[i, j]; // you found it, now remember it
            break;
        }
    }
}

      

No more need for that annoying extra boolean!

Nevertheless, the search for alternative or better solutions continues. I have tried many other methods over the years, but I find they are not that great for one reason or another:

  • Set j

    (the outer loop iterator) to a value higher height

    , which will cause it to break automatically. Not perfect because sometimes you want to remember the meaning i

    and j

    where you found it.
  • Use foreach

    in 2D array. Not ideal as foreach

    it will not allow you to manipulate the collection (delete or add to it, which is very often the reason why I am looking for an object anyway).
  • Just put 2 loops in a function that does nothing but search and return O

    . return

    effectively breaks both loops. This is normal many times, but not always. I do this for very general searches, but there are also quite a few "group searches" where I want to collectivize a move. In these cases, I find 2 or more objects (sometimes within the same 2D array), remember them and only then break out of both loops.
  • Use goto

    ? (Whoa, could this be the only legal use of goto? It's surprisingly readable than a flag KeepLooping

    , especially if we have three or more loops.) Not ideal, because colleagues will scream bloody murder. And in C #, will there be proper garbage cleanup after goto

    ?
  • Throw a custom exception? idk, I've never tried it, but it looks less readable than my preferred way.
  • Once you've found the object you want, do all the manipulations on the object inside the inner loop, and then return;

    It can get messy . Sometimes manipulation of an object includes its own loops.

There is also a very clever 7th way , thanks to User_PWY:

int size = width*height; // save this so you dont have to keep remultiplying it every iteration
for (int i = 0; i < size; i++)
{
   int x = i % width; // ingenious method here
   int y = i / width; // ingenious method here

   O = ObjArray[x, y];

   if (O != null)
       break; // woohoo!
}

      

This effectively compacts the 2D array into one loop for

for iteration. However, some critics have pointed out that mod and division operators are quite slow compared to i ++ or j ++ alone, so it might be slower (remember we're dealing with 2D arrays of someone who knows what size). As I commented, there must be a way to get the division and remainder in a single operation, because I am pretty sure the x86 assembly code has DIV opcodes that store the quotient and the rest in separate registers, all in one DIV instruction. But how to do / use it in C #, idk.

It would be nice if C # allowed you to name loops like L1

and L2

and then do something like L1.break()

; no matter which cycle you are inside. Alas ... it cannot be done in this language. ( Could there be a secret way to do this with macros? ). Will C # 6.0 ever have this feature?

Edit: In my opinion, I judge the solutions by their grace and speed. Remember that we are dealing with nested loops, which can be exponentially huge. Additional operation or comparison can make a difference.

Okay, well, tell me your preferred way, especially if not listed here.

+3


source to share


7 replies


for (int i = 0; i < width*height; i++)
{
   int x=i%width
      ,y=i/width;
   //dostuff
}

      

I like this way of accessing a 2d array.

comment 1)
There have been many comments worrying that the mod (%) operator can be expensive, but this is the whole operation we're talking about and I think it shouldn't have anything to do with another solution.



comment 2)
about the macro. I couldn't find the code, but managed to do it for a while.

#define FOR2DARRAY(WIDTH,HEIGHT) 
    \for (int i = 0, x = 0,y = 0; i < (WIDTH)*(HEIGHT); i++, x=i%(WIDTH),y=i/(HEIGHT))

      

+2


source


Refactoring to avoid deep nesting, perhaps using a LINQ cleaver, is probably the best solution.

Goto

possible for this case (and this is essentially just a use case) if you can't find a better approach. The install icon with a readable name is likely to generate fewer questions / screams.



Not

  • use exceptions for flow control
  • change the loop variables. Figuring out what's going on is very difficult in this case. I would promise that most people will take Goto

    as the best approach.
+1


source


goto

is the perfect solution for this, Microsoft even recommends it :

The goto statement transfers program control directly to the tagged statement.

A common use of goto is to transfer control to a particular label switch box or default label in a switch statement.

The goto statement is also useful for breaking out of deeply nested loops.

Regarding your question about destroying an object, once you are out of scope for the object, the garbage collector should mark it for destruction, but I can't say for sure if the behavior is the same if you use, for example, a using

around for

and goto

out of your loop , as opposed to the usual area.

+1


source


Try the following:

Obj O = ObjArray.Cast<Obj>().FirstOrDefault(obj => obj != null && obj.property == search_value);

      

This will convert the 2D array Obj

to IEnumerable<Obj>

and give you the first one that suits your conditions. If none of them match, is Obj O

set to null

.

Take a look here: https://dotnetfiddle.net/dQTJbU

+1


source


I may have missed it in the parameter list, but why can't you refactor the search into a method that returns your object?

private object FindObject(....)
{
    for (int j = 0; j < height && KeepLooping; j++)
    {
        for (int i = 0; i < width; i++)
        {
            if (ObjArray[i, j] != null && ObjArray[i, j].property = search_value)
            {
                return ObjArray[i, j]; // you found it, now remember it
            }
        }
    }
    return null;
} 

      

+1


source


There are many ways to do this. In your example, I would do it like this:

Obj O = null;

for (int j = 0; j < height && O == null; j++)
{
    for (int i = 0; i < width; i++)
    {
        if (ObjArray[i, j] != null && ObjArray[i, j].property == search_value)
        {
            O = ObjArray[i, j]; // you found it, now remember it
            break;
        }
    }
}

      

In this case, the KeepLooping variable is unnecessary, and therefore a simple break statement works very elegantly.

You could even simplify this:

Obj O = null;

for (int j = 0; j < height && O == null; j++)
{
    for (int i = 0; i < width && O == null; i++)
    {
        if (ObjArray[i, j] != null && ObjArray[i, j].property == search_value)
            O = ObjArray[i, j]; // you found it, now remember it
    }
}

      

No break is required now.

Of course, this may not work for all situations, but often you can use the result type as the state value.

FYI, the problem with changing the collection in foreach is not really a problem if you do it right. For example:

// Assumes ObjArray is [,] and not [][]
foreach (int item in ObjArray.ToList()) // makes a copy to iterate over
{
    if (item != null && item.property == search_value) {
        ObjArray[0,0] = item; // I can now modify this for no reason
        break;
    }
}

      

+1


source


Not very different, but I prefer to use the boolean flag like this:

Obj O = null;
bool found = false;

for (int j = 0; j < height; j++)
{
    for (int i = 0; i < width; i++)
    {
        if (ObjArray[i, j] != null && ObjArray[i, j].property = search_value)
        {
            O = ObjArray[i, j]; // you found it, now remember it
            found = true; // clear the flag so the outer loop will break too
            break;
        }
    }
    if (found) break;
}

if (!found)
{
    // The loop finished with no values found
}
else
{
    // Do stuff here with the value found
}

      

0


source







All Articles