0

I'm writing a graphics program in C# and I couldn't figure out a good way to run a for loop between two values, where either one may be larger or smaller than the other.

To demonstrate, the following code works perfectly when X2>X1:

for (int x = X1; x<=X2; x++) {
    //code
}

However, it fails when X2<X1. What I want to happen in this situation is that the loop starts at X1 and goes backwards until X2.

I since I'm doing a graphics program, I can't simply swap X1 and X2 when X2<X1, as this would mean swapping their associated Y values, which could produce the same problem just for Y values. The loop must always start at X1, it's the direction(+/-) that needs to change, not the order of values.

I've thought of a few solutions however they all have flaws, it's worth noting that X1 will never equal X2.

#1: Replicate loop

if (X2<X1) {
    for (int x = X1; x>=X2; x--) {/*code*/}
} else {
    for (int x = X1; x<=X2; x++) {/*code*/}
}

Unsuitable because of replicated code, especially if the "//code" section is particularly long

#2: Lots of ternaries

for (int x = X1; x!=X2+(X2<X1?-1:1); x+=(X2<X1?-1:1)) {/*code*/}

While this code works and is concise, it's readability is terrible. Also I've seen in various places that using "not equal to" for your loop constraint is bad practice source

#3: Use a while loop

int x = X1;
while(true) {
    //code
    if (X2<X1) {
        x--;
        if (x<X2) break;
    } else {
        x++;
        if (x>X2) break;
    }
}

This solution seems very long and convoluted to perform such a simple task, in addition, use of "while(true)" is also bad practice source

protango
  • 1,072
  • 12
  • 21
  • 1
    Frankly, it is not clear what you are asking here. You seem to have code that works, and there's nothing in your question that would allow anyone to _know_ what kind of answer you'd find acceptable. "Ugly" is in the eye of the beholder; you seem to be asking for nothing more than code that's not "ugly", and with such a subjective standard, any answer might or might not be "right". – Peter Duniho Oct 28 '17 at 04:41
  • @PeterDuniho, point taken, I've included more specific reasons as to why each of my proposed solutions is not ideal for this task. I acknowledge that they all work, but the main problem is good practice coding, conciseness, and readability. – protango Oct 28 '17 at 04:52

6 Answers6

3

I think the most readable option is to simple create/extract method from the repeating code (the first proposed version):

void ComputeRenderedStuff(int x) 
{
   // do your computations for x
}

if (X2<X1) 
    for (int x = X1; x>=X2; x--) 
       ComputeRenderedStuff(x);
else
    for (int x = X1; x<=X2; x++)
       ComputeRenderedStuff(x);
Alexei - check Codidact
  • 22,016
  • 16
  • 145
  • 164
2

A simple solution would be to use one variable for the loop itself, and another variable for the steps:

int length = Math.Abs(x1-x2);
for(int i=0; i <= length; i++)
{
    // step will go either from x1 to x2 or from x2 to x1.
    int step = (x1 < x2) ? x1 + i : x2 + (length-i);
}

Of course, you can wrap the entire loop in a method, so you wouldn't have to repeat the code:

void ForLoopUnknownDirection(int start, int stop, Action<int> action)
{
    int length = Math.Abs(start-stop);
    for(int i=0; i <= length; i++)
    {
        int step = (start < stop) ? start + i : stop + (length-i);
        action(step);
    }
}

This way you can do whatever you want between the numbers while only writing the loop code once.

See a live demo on rextester

Zohar Peled
  • 79,642
  • 10
  • 69
  • 121
1

Simply use Math.Min() and Math.Max() to choose lower and upper boundaries.

Something like this:

int MinX = Math.Min(X1, X2);
int MaxX = Math.Max(X1, X2);

for (int x = MinX; x <= MaxX; x++) {
  //code
}
dotNET
  • 33,414
  • 24
  • 162
  • 251
  • 3
    From the question: _"I can't simply swap X1 and X2 as this would mean swapping their associated Y values, which could produce the same problem just for Y values"_. Your proposal has exactly the effect of swapping the two values, which the OP has already explained will not work for them. – Peter Duniho Oct 28 '17 at 04:39
  • @PeterDuniho: Look at his proposed solutions. All of them are doing that. And he doesn't think they are incorrect, he just said they are ugly. – dotNET Oct 28 '17 at 04:42
  • Also choosing the *associated Y* is something you do INSIDE the loop, using simple math (inverting the array index), which is not what he has asked about. – dotNET Oct 28 '17 at 04:44
  • _"Look at his proposed solutions"_ -- I can't, because his question doesn't actually show _solutions_. It shows little snippets, with important details elided. Frankly, there's not enough code in the question to really know what's needed here. But the fact remains: your proposed solution does exactly the thing that the OP has explicitly stated is not what he's looking for. Which, frankly, is remarkable given how vague the question is on the whole. You've managed to find the one thing we _know_ disqualifies an answer based on the tiny bit of criteria provided in the question. – Peter Duniho Oct 28 '17 at 04:58
  • @PeterDuniho I don't think the question needs any more context, it's quite clear what is trying to be accomplished here. Other programming languages like python and MATLAB have this exact feature built into their for loop constructs so clearly there are use cases for doing this. – protango Oct 28 '17 at 05:03
  • @PeterDuniho his answer doesn't do "the one thing op didn't want", it doesn't swap X1 and X2 in the for loop but puts them in a given order which seems to be exactly what the OP wanted (that is swap them or not swap them depending on their relative value) which is not the same as simply swapping them and clearly fits the only part of the question that is clear "run a for loop between two values, where either one may be larger or smaller than the other" – Ronan Thibaudau Oct 28 '17 at 05:03
  • @RonanThibaudau I've clarified the question to demonstrate that it's the *direction* between X1 and X2 that needs to change, not the order. The loop must always start at X1. – protango Oct 28 '17 at 05:09
  • @user45940 - Inside loop you could have `int wantedX = X1 < X2 ? x : (X1 - (x - X2));`. – Corak Oct 28 '17 at 06:59
1

Maybe extract a method like this

private static IEnumerable<int> Step(int start, int end)
{
    if (start < end)
    {
        for (int x = start; x <= end; x++)
            yield return x;
    }
    else
    {
        for (int x = start; x >= end; x--)
            yield return x;
    }
}

Then you can do

foreach (int x in Step(X1, X2))
{
    /*code*/
}
Ulf Kristiansen
  • 1,571
  • 3
  • 22
  • 34
  • While this is a nice solution, it runs two loops. other solutions such as Alexei's or mine should out perform it (not by much, though). – Zohar Peled Oct 28 '17 at 17:29
0

Use a directional increment ( d in the code below )

var d = (x1 > x2) ? -1 : 1;
var i = x1;
while (i != x2)
{
   //
   // insert your code here
   //
   i = i + d;
}

Am I purdy?

Goose
  • 546
  • 3
  • 7
-1

Why so complicated?

for (int n = 0; n < Count; n++)
{
   int Index = (ascending ? n : Count - 1- n);
}