For Loop Question

Let’s say I want to start loop in the middle, then after it gets to the end, restart it and get to the starting point.

For example, there’s an array of 12, I want to start at 6 and basically end at 5 (do a wrap around). If I simply do

for (int i = 6; i < array.Length; i++)

it will start at 6 and end at 12.

Before starting to mess around with while loops, I wanna make sure is there’s is a simpler solution.

first thought would be using additional index variable, and wrap that around:

int k=6;
for (int i = 0; i < array.Length; i++)
{
k=k++%array.Length;
}
6 Likes

Probably also possible with a do-while loop:

int[] ints = new int[12];
int midPoint =  ints.Length / 2;
int index = midPoint + 1;

do
{
    // do stuff
    index = (index + 1) % ints.Length;
}
while (index != midPoint);

Did not test at all though.

2 Likes

A for loop can have more than one variable as long as you define the type outside of the loop. With that in mind we can have a separate variable tracking the conditional while the variable we want to be a certain range can just be initialized and modified. I wouldn’t necessarily call this a simpler approach though.

int i, j;
for (i = startIndex, j = 0; j != array.Length; i = (i + 1) % array.Length, j++)
2 Likes

Wow this actually perfect! It will take me a while to figure out the exact logic, but simple debug shows it does indeed wrap around nice and neat. Thank you very much. I think it’s simpler than while loops, I always manage to crash Unity with those.

This sorta stuff…

When Ryiah says:

I would say it even stronger: keep the loop control logic dead simple: one variable, ideally going from zero to max one number at a time.

Then do things at the top of the loop like use modulus (%) operator to get your values, pull up table entries, scale the stepping, etc.

YES there are exceptions, but for the simple “start from middle, go to just before middle” thing you specifically asked about, I would use the offset → modulus approach that mgear and Spiney posted.

All for() loops are just while() loops underneath. Really.

Here’s why your code locks up:

Unity will lock up 100% of the time EVERY millisecond your scripting code is running.

Nothing will render, no input will be processed, no Debug.Log() will come out, no GameObjects or transforms will appear to update.

Absolutely NOTHING will happen… until your code either:

  • returns from whatever function it is running

  • yields from whatever coroutine it is running

As long as your code is looping, Unity isn’t going to do even a single frame of change. Nothing.

No exceptions.

“Yield early, yield often, yield like your game depends on it… it does!” - Kurt Dekker

1 Like

You can cause an infinite loop with a for too. I ended up creating a couple while testing that line and crashing the online code tester (https://try.dot.net/). :stuck_out_tongue:

Here’s another common approach using a recursive method. While I’m not a fan of recursion I think this is easier to read than the for loop I provided earlier but partially because it’s not crammed into a single line. The catch with recursion is that it’s possible to run out of stack space and cause a crash that way.

void LoopFromIndex(int[] array, int start, int stop)
{
    // Check if start index is out of bounds.
    if (start < 0 || start >= array.Length)
        return;

    Console.WriteLine(start);

    // Move to the next index, wrapping around if necessary.
    int nextIndex = (start + 1) % array.Length;

    // Stop recursion if we've reached the stop value.
    if (start == stop)
        return;

    LoopFromIndex(array, nextIndex, stop);
}

I definitely prefer @mgear 's approach. One loop variable, clear from reading what you’re doing, compact and concise.

2 Likes

I tested the method with 2 variables by @Ryiah and it worked perfectly for what I need. Now I have to admit I didn’t quite understand @mgear method, it seems to start with 0 (which isn’t what I need) and I don’t know enough of loops to figure it out.

The reason I don’t wanna do more complicated things is there are already 2 for loops and I don’t need more complications.

If anyone is interested in what I’m trying to do: imagine an inventory system in Stardew Valley (or Minecraft), where you can switch current active hotbar. When an item is added, it first searches for any slot with this particular item (1st loop), if there isn’t any it searches for an empty slot (2nd loop).
If the first hotbar is active then searching from 0 makes sense, however if another hotbar is active, it has to start searching from slots that belong to the current active hotbar.

Since method with 2 vars works and it’s basically a one line, I don’t see a problem with it.

In their version, the “i” variable is just used as a counter to loop through the size of the array. You would then use “k” as the index of the array within the loop.

1 Like

When I do this

int k = 6;
        for (int i = 0; i < array.Length; i++)
        {
            k = k++ % array.Length;
            print(k);           
        }

k is always 6 throughout the loop. Am I missing something?

That should probably read k = (k+1) % array.Length;. The use of the incrementor (++) simultaneously with an assignment is not appropriate. The assignment happens with k’s original value, after the increment happens, so no increment is kept. I didn’t notice that in @mgear 's code previously.

https://stackoverflow.com/questions/33783989/post-increment-within-a-self-assignment

5 Likes

Ah yes, it works now, thank you. I need to learn more about % operator.

It’s returning the amount left over from the division of the left value by the right value.

Debug.Log(5 / 3); // returns 1 because 3 goes into 5 once
Debug.Log(5 % 3); // returns 2 because 5 divided by 3 leaves 2

Here’s a method version of it if you want to see the formula.

public int Modulo(int a, int b)
{
    return a - ((a / b) * b);
}

Modulo’s primary advantage is being able to create a looping effect without a conditional like an if which is an optimization that in some cases can be significant.

for (int i = 0; i < 15; i++)
{
    Debug.Log(i % 5);
}

Outputs.

0
1
2
3
4
0
1
2
3
4
0
1
2
3
4
        for (int i=0;i<array.Length;i++)
            Debug.Log(array[(i+6)%array.Length]);
1 Like

I feel like the standard method is to compute the adjusted value. This would be the version with int i2=(i+6)%A.length inside the loop. That makes it easier to understand that there’s only 1 moving value, going through the array, and you’re just using it a little differently. Or put another way, we can quickly see that i2 isn’t just doing it’s own thing - it’s 100% based on the main loop variable.

4 Likes

Yes, that would be my approach as well. Computationally we’re doing the same thing anyways but we do not need to track the variable separately, so it can be a local variable. Instead of adding 1 each iteration and wrapping it around, we can simpy add the offset (6) to the current for loop variable and wrap it around to get our current shifted index.

I’ve also seen people using a foreach loop and tracking their own index in parallel. For me this is a complete anti-pattern as it’s not always guaranteed that however the foreach loop actually iterates, that it matches with the parallel tracked index.

That said, a struct enumerable could also solve this

    public struct ShiftedIterator
   {
       public static ShiftedIterator Get(int aOffset, int aRange) => new ShiftedIterator(aOffset, aRange);
       int m_Offset, m_Index, m_Range;
       public ShiftedIterator GetEnumerator() => this;
       public int Current => (m_Index + m_Offset) % m_Range;
       public bool MoveNext() => ++m_Index < m_Range;
       public ShiftedIterator(int aOffset, int aRange)
       {
           m_Offset = (aOffset<0)?(aRange+aOffset%aRange):aOffset;
           m_Range = aRange;
           m_Index = -1;
       }
   }

See it here in action (.NET Fiddle).
The offset can be any positive or negative “shift”. So at a shift of “-1” and a range / count of 10 it would iterate 9, 0, 1, 2, 3, 4, 5, 6, 7, 8. So the output will always be in the range
0 to range-1 but shifted & rotated by the given amount.

I was thinking about creating an extension method for integer, however it’s difficult to choose a good “direction” and name for the method. So should it be

array.Length.Iter(6)

or should it be

6.Iter(array.Length)

I guess the first one would make most sense, but choosing a meaningful name is kinda difficult in order to not be too verbose. So for the sake of clarity it’s probably best to not go there. Here something like a Rotate would make more sense

for(int i = 0; i < array.Length; i++)
{
    int idx = i.Rotate(6, array.Length);
}

Though I don’t think that’s worth it when you could just do

for(int i = 0; i < array.Length; i++)
{
    int idx = (i+6) % array.Length;
}

Yes, you can not as easily rotate in the negative direction, but I don’t think that’s required that frequently anyways :slight_smile:

1 Like

I can’t prove it, but I think the “foreach with hacked-in index” is from people who started with foreach, especially in some other language that may not even have a for(;;). They learn foreach for lists, while for weird stuff (like finding x^y), and anything else is rare. For the OP’s problem they might prefer to put the first 6 items on the end, then use a simple foreach.

Or maybe they’ve seen the trick with for (x,i) in addIndexs(L): (a foreach loop with an extra part to get the index), but they can’t find it in C#. They have to manually do int i=0; i++ and are like “geez, doesn’t anyone in C# ever need to use indexes? There’s no language support!”

I’ll throw yet another option into the mix, just to add to the chaos :wink:

foreach(var item in array[6..].Concat(array[0..6]))

Or if this is in a hot path, a less readable but more performant variant:

var items = array.AsSpan();
foreach(var item in items[6..])
{
    LocalFunction(item);
}

foreach(var item in items[0..6])
{
    LocalFunction(item);
}
1 Like