4

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

Something I come across time and time again is a nested loop, searching through some 2D array to find an element (typically some object), which then has to be manipulated. So of course, once you find that object, you should break out of both loops so you don't needlessly continue the search for something you already found (especially in nested loops which can traversing exponentially huge arrays).

The following code is currently my preferred way of doing it:

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 Erik 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 pesky extra boolean!

Nevertheless, the search for alternative or better solutions goes on. Over the years I've tried many other ways, but find them not that great for one reason or another:

  1. Set j (the outer-loop iterator) to a value above height, which will trigger it to break automatically. Not ideal because sometimes you want to remember the i and j values where you found it.
  2. Use foreach on the 2D array. Not ideal since foreach won't let you manipulate the collection (delete or add to it, which very typically is the reason I search for the object anyway).
  3. Just put the 2 loops in a function that does nothing but find and return O. return effectively breaks both loops. Many times this is okay, but not always. I do this for very generic searches, but there are also quite a lot of "group searches" where I want to collectivize the traversing. In those cases, I find 2 or more objects (sometimes within the same 2D array), remember them, and only then break out of both loops.
  4. Use a goto? (Whoa, could this be the only legit use of a goto? It's surprisingly more readable than the KeepLooping flag, especially if we have 3 or more loops.) Not ideal because coworkers will scream bloody murder. And in C#, will there be correct garbage cleanup after the goto?
  5. Throw a custom exception? idk, i've never tried it, but it looks less readable than my currently preferred way.
  6. Once you've found the right object, do all your object manipulation inside the inner loop, and then return; This could get messy fast. Sometimes the object manipulation involves 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 for loop for iteration. However, some criticism has pointed out that the mod and division operators are pretty slow in comparison to just i++ or j++, so it could be slower (remember we are dealing with 2D arrays of who-knows-what size). Like i commented, there should be a way to get the division and remainder in one operation because I'm pretty sure the x86 assembly code has DIV opcodes that store the quotient and remainder in separate registers, all in one DIV instruction. But how to do/use that in C#, idk.

It would be nice if C# allowed you to name loops, such as L1 and L2, and then do something like L1.break(); regardless of which loop you're inside. Alas...it cannot be done in this language. (Could there be some secret way of doing this using macros?) Is there ever gonna be a C# 6.0 with this feature implemented?

Edit: in my opinion, I judge solutions on their elegance and speed. Remember we are dealing with nested loops, which can get exponentially huge. An extra operation or comparison could make a difference.

Okay, well, tell me your preferred way, especially if it's something not listed here.

DrZ214
  • 486
  • 5
  • 19
  • `goto` is in C# for a reason, so if you feel that it's a legitimate use case for goto and have the performance numbers to prove it, your colleagues shouldn't be complaining. – zzzzBov May 18 '15 at 00:52
  • 1
    `goto` does not change how garbage collector works. – MarcinJuraszek May 18 '15 at 01:06
  • @zzzzBov okay but it's hard to tell colleagues what they should and should not be complaining about, unless you're the boss (i'm not) :/ – DrZ214 May 18 '15 at 03:46
  • 1
    It's easy to tell your colleagues they can't complain when you have proof. Then all you have to ask for is evidence that they have a better method. If they do, use that instead. – zzzzBov May 18 '15 at 13:19

7 Answers7

3

goto is a perfectly fine solution for this, Microsoft even recommends it:

The goto statement transfers the program control directly to a labeled statement.

A common use of goto is to transfer control to a specific switch-case label or the default label in a switch statement.

The goto statement is also useful to get out of deeply nested loops.

As for your question about object destruction, once you are out of scope for a scoped object the garbage collector should mark it for destruction, but I can't say for sure whether the behavior is the same if you use, for example, a using directive around your for loop and goto out of it as opposed to exiting scope normally.

IllusiveBrian
  • 3,105
  • 2
  • 14
  • 17
2

Refactoring to avoid deep nesting, possibly with cleaver usage of LINQ is probably better solution.

Goto is possible for this case (and this is essentially only case to use it) if you can't come up with better approach. Setting flag with readable name will likely cause less questions/screaming.

Do not

  • use exceptions for flow controls
  • change loop variables. Figuring out what happens is very hard in this case. I'd bet most people will accept goto as much better approach.
Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
  • Breaking out of nesting in LINQ is pretty tricky without exceptions. Can you show an example? – Gabe May 18 '15 at 03:53
  • @Gabe one option - `SelectMany` to flatten nested loops and `TakeWhile`/`SkipWhile`/`First`/`Where` to filter/cut loop short – Alexei Levenkov May 19 '15 at 00:23
2
for (int i = 0; i < width*height; i++)
{
   int x=i%width
      ,y=i/width;
   //dostuff
}

I like this way of access to 2d array.

comment 1)
There were many comments worrying that mod(%) operator could be costly, but this is integer operation we are talking about and I think that it should make no difference other solution.

comment 2)
about the macro. I couldn't find the code but managed to produce one briefly.

#define FOR2DARRAY(WIDTH,HEIGHT) 
    \for (int i = 0, x = 0,y = 0; i < (WIDTH)*(HEIGHT); i++, x=i%(WIDTH),y=i/(HEIGHT))
YukiNyaa
  • 136
  • 1
  • 13
  • Nice. It seems vaguely familiar, I may have used it once or twice in messing with arrays for some other purpose. Now I really wanna go hunting through my archives...but i'm sure i've never seen or heard of it for breaking nested loops, so i marked it. Does this work for any width x height? or does it have to be a square? – DrZ214 May 18 '15 at 03:50
  • The one issue with this solution is that now you are doing two relatively expensive operations (division and remainder) vs one cheap operation (addition) per iteration. That can be bad for perf sensitive loops. – Mike Zboray May 18 '15 at 03:53
  • iirc, the mod operator is not very expensive at all. Division certainly is. As for the multiplication in the loop header, we can just do something like `int size = width*height` so we do the multiplying just one time, then remember it, and use `size` in the loop header instead of the more literal `witdth*height`. – DrZ214 May 18 '15 at 04:13
  • @DrZ214 My understanding is that remainder is similar in expense to division. However [on x86](http://stackoverflow.com/a/7070383/517852) the remainder is a by-product of division so I wouldn't be surprised if the JIT optimized it into one operation (although one of the comments there suggests that it isn't in .NET). Yes I intentionally ignored the multiplication because that is easily removed. – Mike Zboray May 18 '15 at 04:38
  • @mikez apparently i misunderstood `a mod b` to be just subtracting b from a until a < b, then returning that remainder. Althought it can be done this way, after checking it out, it seems that CPUs actually computer a remainder very similary to how they do division. However, i remember from my assembly days that the division operations usually get bot a quotient and a remainder, so is it possibly we could compact this further by doing only one operation to get both values somehow? – DrZ214 May 18 '15 at 04:48
  • @User_PWY thanks this is pretty good, but can you give an example of macros? like you said you use macros in C++ – DrZ214 May 18 '15 at 04:49
  • 2
    @DrZ214 You would think there would be, but I don't think there is. There is the attractively named `Math.DivRem` however people have [opened issues](https://github.com/dotnet/coreclr/issues/757) because it effectively does the underlying division twice. The [source](http://referencesource.microsoft.com/#mscorlib/system/math.cs,5a74d22efbb031e3) is very similar to this code, so it looks like the JIT does not optimize this case. Hopefully, it will get addressed soon. – Mike Zboray May 18 '15 at 05:10
1

Not very much different, but I prefer to use the boolean flag in this way:

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
}
Blas Soriano
  • 566
  • 3
  • 16
1

Try this:

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

This converts the 2D array of Obj into an IEnumerable<Obj> and gives you the first one that matches your conditions. If none of them match, Obj O is set to null.

Check it out here: https://dotnetfiddle.net/dQTJbU

Kaz
  • 721
  • 3
  • 15
1

Maybe I missed it in your list of options, 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;
} 
Ray
  • 45,695
  • 27
  • 126
  • 169
  • Yep, that's solution number 3 I listed. I use it around half the time, but it's not always adequate. See listed item 3 for why. – DrZ214 May 18 '15 at 03:44
  • @DrZ214 - Ok, but I don't see why, if you want more objects, just make the function return a list. – Ray May 19 '15 at 01:49
1

There are many 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;
        }
    }
}

The KeepLooping variable is unnecessary in this case, and thus the simple break statement works very elegantly.

You could actually even simplify this more:

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

Now, the break isn't even required.

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

FYI, the issue with modifying the collection in a foreach isn't really a problem if you do it correctly. For instance:

// 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;
    }
}
Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291
  • Hey thanks for that even shorter method using `&& O == null` instead of `KeepLooping`! Much more elegant without an extra boolean. But I would not do it as in your second example. The `break;` saves us a few extra instructions by breaking immediately instead of having to go back and make that comparison. Also, putting `&& O == null` in the inner loop also means we are doing that comparison every iteration, when really, it's only necessary in the outer loop. – DrZ214 May 18 '15 at 04:18
  • @DrZ214 - You didn't make it clear that you cared about a few extra cpu cycles. Still, comparison operations are cheap so long as they are intrinsic types. – Erik Funkenbusch May 18 '15 at 04:26
  • Okay i went back and made it clear. I like your first solution and added it to my OP. – DrZ214 May 18 '15 at 04:36