View unanswered posts | View active topics It is currently Mon Dec 22, 2014 6:40 am






Reply to topic  [ 10 posts ] 
simplifying multiple 'if' statements 
Author Message
Rookie

Joined: Thu Mar 13, 2014 6:12 am
Posts: 18
Post simplifying multiple 'if' statements
Is it possible to simplify the following code?
Code:
if(range1 > range2)
{
if(range1 > range3)
{
if(range1 > range4)
{
if(range1 > range5)
{
}
}
}
}

I am writing a programme, and one thing it has to do is compare 20 different variables! I am just wondering if there is a way to simplify the code, so that it is easier to program.

Thanks, in advance, to anyone who can help!


Tue Jul 08, 2014 4:37 am
Profile
Rookie

Joined: Wed Feb 24, 2010 11:51 pm
Posts: 24
Post Re: simplifying multiple 'if' statements
What problem are you trying to solve? Often, understanding the original problem helps to find the best solution.

If it turns out that you're trying to find where a value fits in a sorted sequence, then you'd be better off using an array and a loop like this:
Code:
int value = 5;
int ranges[20] = {1, 2, 3, 4, 5, ... , 19, 20}; // you have to fill in all the values, I cut out the middle for brevity

int i;
for (i = 0; i < 20; ++i) {
  if (value <= range[i])
    break;
}
// i now holds the index that points to the correct value in the array

_________________
Vex Coach and robotics instructor


Tue Jul 08, 2014 11:49 am
Profile
Rookie

Joined: Thu Mar 13, 2014 6:12 am
Posts: 18
Post Re: simplifying multiple 'if' statements
OK. I forgot to add one part to my example code :o . What it needs to do, is figure out which variable has the lowest number in it (I figured out that it should've been the lowest, instead of the highest). The example code should've looked like this:
Code:
if(a < b)
{
if(a < c)
{
//a is the lowest
}
else
{
//c is the lowest
}
}

else
{
if(b < c)
{
//b is the lowest
}
else
{
//c is the lowest
}
}


The problem I'm facing is, that to compare 20 variables, I have to have giant if and else statements. I'm not finished writing the programme yet, and it's already over 1000 lines long!


Tue Jul 08, 2014 9:56 pm
Profile
Rookie

Joined: Thu Mar 13, 2014 6:12 am
Posts: 18
Post Re: simplifying multiple 'if' statements
Here is my test programme. It's meant to scan, recording 5 variables/locations throughout. After the scan, it's meant to calculate which location was closest, and then turn to that location.
Code:
#pragma config(Sensor, S1,     tch,            sensorTouch)
#pragma config(Sensor, S2,     lght2,          sensorCOLORRED)
#pragma config(Sensor, S3,     lght3,          sensorCOLORRED)
#pragma config(Sensor, S4,     ultra,          sensorSONAR)
#pragma config(Motor,  motorA,          arm,           tmotorNXT, PIDControl, encoder)
#pragma config(Motor,  motorB,          mtrb,          tmotorNXT, PIDControl, reversed, encoder)
#pragma config(Motor,  motorC,          mtrc,          tmotorNXT, PIDControl, reversed, encoder)
//*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//"

task main()
{
int a;                              //set the variables
int b;
int c;
int d;
int e;
int lowest;
int movee;

nMotorEncoder(mtrb) = 0;

while(nMotorEncoder(mtrb) < (0 - (5000000)) && e == 0)    //the 5000000 was done for testing purposes
{
motor(mtrb) = -30;                          //turns, and stops
motor(mtrc) = 30;
wait10Msec(1000);
motor(mtrb) = 0;
motor(mtrc) = 0;

if(a == 0)                                       //sets the variables/locations
{
a = SensorValue(ultra);
}

else if(b == 0)
{
b = SensorValue(ultra);
}

else if(c == 0)
{
c = SensorValue(ultra);
}
else if(d == 0)
{
d = SensorValue(ultra);
}

else if(e == 0)
{
e = SensorValue(ultra);
}

}

if(a < b)                                     //comparing logic. I'm not sure i got this completely correct
{
if(a < c)
{
if(a < d)
{
if(a < e)
{
lowest = 1;
}
}
else
{
if(d < e)
{
lowest = 4;
}
else
{
lowest = 5;
}
}
}
else
{
if(c < d)
{
if(c < e)
{
lowest = 3;
}
else
{
lowest = 5;
}
}
else
{
if(d < e)
{
lowest = 4;
}
else
{
lowest = 5;
}
}
}
}

else
{
if(b < c)
{
if(b < d)
{
if(b < e)
{
lowest = 2;
}
else
{
lowest = 5;
}
}
else
{
if(d < e)
{
lowest = 4;
}
else
{
lowest = 5;
}
}
}
else
{
if(c < d)
{
if(c < e)
{
lowest = 3;
}
else
{
lowest = 5;
}
}
else
{
if(d < e)
{
lowest = 4;
}
else
{
lowest = 5;
}
}
}
}

if(lowest == 1)                            //set the movement variable
{
movee = 4;
}

if(lowest == 2)
{
movee = 3;
}

if(lowest == 3)
{
movee = 2;
}

if(lowest == 4)
{
movee = 1;
}

if(lowest == 5)
{
movee = 0;
}

while(movee > 0)             //turn back to the position, that had the smallest variable
{
motor(mtrb) = 30;
motor(mtrc) = -30;
wait10Msec(10);
motor(mtrb) = 0;
motor(mtrc) = 0;

movee--;
}

}


When I run the programme, all the robot does is stay still! The programme runs for exactly 1 second (I timed it). Could anyone tell me what's wrong with my programme? Thanks!


Wed Jul 09, 2014 3:39 am
Profile
Rookie

Joined: Wed Feb 24, 2010 11:51 pm
Posts: 24
Post Re: simplifying multiple 'if' statements
You should definitely be using a loop to find the lowest value in the set. If you choose to record all the samples and review them later, then you have to use an array. But that's not necessary for your application. All you need to remember is the closest thing and which heading pointed to that thing.

One problem I see is that your main loop says "while motor encoder < -5000000". This can't work because the SensorValue array is of type signed short, which means it can only have values from -32768 to 32767. Comparing against -5000000 will always be false and the loop will never run, the program will exit immediately. On NXT, programs always show as "running" for 1 second after your code ends. That's what you're seeing.

The ultrasonic sensor returns a value for the range to target or a value that indicates "no echo heard". Since "no echo" is effectively the same thing as "really far away", it would be most convenient if the "no echo heard" value were bigger than any possible real measurement. But if you read the result directly from SensorValue[ultra], which is a signed short, it looks like -1, which is smaller than any possible real measurement. This is very inconvenient as it complicates the code needed to deal with it. Fortunately, if you interpret the same value as an unsigned short, it reads as 65535. This is because the signed value -1 and the unsigned value 65535 are encoded using the same 16 bit pattern (all ones -> 1111111111111111). Interpreted as an unsigned short, the "no echo" value is biggest, so the logic gets simpler. It's just a matter of applying the correct interpretation. You should always consider this when using SensorValue[]. Sometimes signed short is better, sometimes unsigned short.

As for the rest of the algorithm, I'll write the loop and test logic and leave the rest up to you.

Code:
int closePosition = 0;
unsigned short closeRange = 65535; // this is the value the sonar returns for "no echo heard"

// Scan range on 5 different headings
//
int position;
for (position = 0; position < 5; ++position) {
    unsigned short range = SensorValue[ultra]; // need to store in unsigned short to get correct readings, including "no echo heard"
    if (range < closeRange) {
        closeRange = range;
        closePosition = position;
    }
    turnLeftALittle();
}

// Turn back to closest heading
//
for (; position > closePosition; --position) {
    turnRightALittle();
}

// Drive toward target
//
driveForward();

_________________
Vex Coach and robotics instructor


Wed Jul 09, 2014 12:16 pm
Profile
Rookie

Joined: Thu Mar 13, 2014 6:12 am
Posts: 18
Post Re: simplifying multiple 'if' statements
Thanks, again! I edited your code (a bit) and the programme works quite well, now! One last question (hopefully). Will the Ultrasonic Sensor be more accurate when each 'turn unit' is faster, or slower?

Here's my edited code:

Code:
#pragma config(Sensor, S1,     tch,            sensorTouch)
#pragma config(Sensor, S2,     lght2,          sensorCOLORRED)
#pragma config(Sensor, S3,     lght3,          sensorCOLORRED)
#pragma config(Sensor, S4,     ultra,          sensorSONAR)
#pragma config(Motor,  motorA,          arm,           tmotorNXT, PIDControl, encoder)
#pragma config(Motor,  motorB,          mtrb,          tmotorNXT, PIDControl, reversed, encoder)
#pragma config(Motor,  motorC,          mtrc,          tmotorNXT, PIDControl, reversed, encoder)
//*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//

task main()
{

int scansweeprangeunits = 100;              //set the two variables, which set two main factors in the programme
int scansweepdistmax = 50;

int closePosition = 0;
unsigned short closeRange = 65535;
int position;


for(position = 0; position < scansweeprangeunits; ++position)
{
unsigned short range = SensorValue(ultra);


int fixer = closeRange;                        //this is so that 'scansweepdistmax' can correctly compare against 'closeRange'
bool candetected1;

if(fixer > scansweepdistmax)
{
candetected1 = false;
}

else
{
candetected1 = true;
}

if(range < closeRange && candetected1)         //the '&& candetected1' is so a maximum distance can be set, through the 'scansweepdistmax' variable
{
closeRange = range;
closePosition = position;
}

motor(mtrb) = -40;                                     //a 'turn unit' part. It consists of the motor turning for 50 msecs, and then stopping.
motor(mtrc) = 40;
wait1Msec(50);
motor(mtrb) = 0;
motor(mtrc) = 0;
}




for(; position > closePosition; --position)
{
motor(mtrb) = 40;                                           //another 'turn unit' part
motor(mtrc) = -40;
wait1Msec(50);
motor(mtrb) = 0;
motor(mtrc) = 0;
}

}


terbos wrote:
for (position = 0; position < 5; ++position)


terbos wrote:
for (; position > closePosition; --position)


Is '++position' and 'position++' the same thing? I'm just wondering, as I have only ever seen it written as '~++' before! Same with '--position', and '~--'.

Thanks for all the help!


Mon Jul 14, 2014 9:36 pm
Profile
Rookie

Joined: Wed Feb 24, 2010 11:51 pm
Posts: 24
Post Re: simplifying multiple 'if' statements
PACteam wrote:
Thanks, again! I edited your code (a bit) and the programme works quite well, now! One last question (hopefully). Will the Ultrasonic Sensor be more accurate when each 'turn unit' is faster, or slower?

Glad to hear it works.

I'm not quite sure what you mean by, "turn unit is faster or slower". You could mean using a different motor power to turn more or less quickly. You could also mean turning for more/less time between sampling positions. In either case, I cannot say what the real answer is. This seems to be an excellent opportunity for you to do an experiment where you graph the speed of turning vs. the number of position stops and record the accuracy.

PACteam wrote:
Code:
int scansweepdistmax = 50;
...
int fixer = closeRange;                        //this is so that 'scansweepdistmax' can correctly compare against 'closeRange'


You might consider making the scansweepdistmax constant to be of type "unsigned short" so it matches with the range measurement. Forcing the range measurement to match the constant is backwards thinking.

PACteam wrote:
Code:
bool candetected1;

if(fixer > scansweepdistmax)
{
candetected1 = false;
}

else
{
candetected1 = true;
}


Because you are always assigning a value to candetected1 and it's a boolean result, it's typically cleaner to use an expression like this:
Code:
bool candetected = range <= scansweepdistmax;

Note that the '<=' is evaluated before the '=', so the result of the expression "range <= scansweepdistmax" is then assigned into candetected.

If you want to have a reference for the order that expressions are evaluated, search online for "C operator precedence table" and print out one of the charts. I always keep one handy.

PACteam wrote:
Code:
motor(mtrb) = -40;                                     //a 'turn unit' part. It consists of the motor turning for 50 msecs, and then stopping.
motor(mtrc) = 40;
wait1Msec(50);
motor(mtrb) = 0;
motor(mtrc) = 0;


Since your program declares the motors as "PIDControl", you might consider using that feature for your turns instead of time. They'll be more consistent and also be unaffected by turn direction, battery condition, and changes in surface type.

PACteam wrote:
terbos wrote:
for (position = 0; position < 5; ++position)


terbos wrote:
for (; position > closePosition; --position)


Is '++position' and 'position++' the same thing? I'm just wondering, as I have only ever seen it written as '~++' before! Same with '--position', and '~--'.

Thanks for all the help!

The two ways of writing this are called:
- pre-increment - ++variable - remember it because the ++ is prefixed to the variable name
- post-increment - variable++ - remember it because the ++ is postfixed (suffixed) to the variable name

If the thing you're incrementing is an isolated statement: it's sitting by itself on its own line, or in the third position of a for-loop, then they have the same effect, i.e. they both just increment the variable. However, if you're using the increment as part of an expression, then it makes a big difference which you choose.

Here's the difference:
- no increment
Code:
int x = 1;
int y = x;

// x is 1
// y is 1


- pre-increment - increment the variable, then use its new value
Code:
int x = 1;
int y = ++x;

// x is 2
// y is 2


- post-increment - set aside a copy of the original value, increment the variable, then use the old value
Code:
int x = 1;
int y = x++;

// x is 2
// y is 1


So to answer the question of why I prefer to use pre-increment in for-loops when the result is the same:
- post-increment makes an extra copy of the value, which then gets discarded. This is wasted effort which you avoid by using pre-increment.
- It's true that most modern compilers will optimize away the extra copy so the net result is identical code, but if you start working in C++ with objects bigger and more expensive to copy than simple ints, it's a very good habit to always use the pre-increment (unless you really need to use the original value as part of an expression). So you might as well start now.

btw, the same rules apply to pre-decrement: --variable and post-decrement: variable--

Terry

_________________
Vex Coach and robotics instructor


Tue Jul 15, 2014 1:53 pm
Profile
Rookie

Joined: Thu Mar 13, 2014 6:12 am
Posts: 18
Post Re: simplifying multiple 'if' statements
Thanks, again!


terbos wrote:
You might consider making the scansweepdistmax constant to be of type "unsigned short" so it matches with the range measurement.


Will leaving it as it is make the programme have different results (leaving it as signed short)? I just find it easier to understand, when it is " Forcing the range measurement to match the constant ".

terbos wrote:

PACteam wrote:

Code:
bool candetected1;

if(fixer > scansweepdistmax)
{
candetected1 = false;
}

else
{
candetected1 = true;
}






Does this also affect the accuracy? I agree that
Code:
bool candetected = range <= scansweepdistmax;
is a lot neater, but (as before), I find it currently 'makes more sense'.



terbos wrote:
Since your program declares the motors as "PIDControl", you might consider using that feature for your turns instead of time. They'll be more consistent and also be unaffected by turn direction, battery condition, and changes in surface type.


Lastly, would you recommend using a "for" statement, or a "while" statement, for using PIDcontrol? It's just that if you use a "for" statement, then (to my knowledge), it would be more complex than the latter option! I believe it would look like this:

Code:
nMotorEncoder(mtrb) = 0
for(;nMotorEncoder(mtrb) > -15;)          //'-15' is just a guess. '-10' might be better. Not sure.
{
motor(mtrb) = -40;
motor(mtrc) = 40;
}


Isn't that just a while statement, as the 'condition1' and 'condition3' phrases aren't used?!

Thanks, again, for all the answers!


Thu Jul 17, 2014 9:12 am
Profile
Rookie

Joined: Wed Feb 24, 2010 11:51 pm
Posts: 24
Post Re: simplifying multiple 'if' statements
PACteam wrote:
Will leaving it as it is make the programme have different results (leaving it as signed short)? I just find it easier to understand, when it is " Forcing the range measurement to match the constant ".

The program works the same way given either option. But as you learn more about C programming, you'll run into the issue of signed vs. unsigned and integer storage sizes quite often. I've found that keeping the code as simple as possible is the most important part of getting it right. A big help in keeping things simple is for a given set of constants and variables that interact, make sure that they're always the same type (signed/unsigned & size). Mixing and matching types will often lead to confusing behavior.

In any RobotC program, SensorValue[] is always of type "signed short". The makers of RobotC chose that type and you're stuck with it. But as you know, when you read a value from SensorValues[] and store it into a variable, you can choose whatever type you want for that variable. It's very important to know how C treats the value when converting it from one type to another. I found an explanation here: https://www.securecoding.cert.org/confluence/display/seccode/INT02-C.+Understand+integer+conversion+rules and as you can see, it's a bit complicated.

To avoid all that complication, I prefer keeping things simple. Since we're stuck with SensorValue[] as a short, then it makes sense to have all the variables/constants/math/comparisons also be short. That takes care of any size mismatches. For signed/unsigned, you have some flexibility there, but as I explained before, it's easiest to handle the "echo not heard" condition as an unsigned.

That's the reasoning I used to recommend using "unsigned short" as the type of the scansweepdistmax constant.

PACteam wrote:
Does this also affect the accuracy? I agree that
Code:
bool candetected = range <= scansweepdistmax;
is a lot neater, but (as before), I find it currently 'makes more sense'.

The behavior between the two coding options is identical, so either will work.

I understand that since you're just learning C, it's easier for you to understand the if/else form. But as you get more comfortable with the language, one of the most powerful features you'll learn is how to use simple expressions in place of larger code blocks. I picked on this as a good example specifically because of the pattern where you're using if/else to assign true/false into a variable.

Think about it this way:
- The expression "range <= scansweepdistmax" is an operation that combines two integer values and produces a boolean result.
- You can use that boolean result in one of two ways:
-1- to control an if statement and choose between assigning true or false into a boolean variable
-2- assign the boolean result of the expression directly into the variable

PACteam wrote:
Lastly, would you recommend using a "for" statement, or a "while" statement, for using PIDcontrol? It's just that if you use a "for" statement, then (to my knowledge), it would be more complex than the latter option! I believe it would look like this:

Code:
nMotorEncoder(mtrb) = 0
for(;nMotorEncoder(mtrb) > -15;)          //'-15' is just a guess. '-10' might be better. Not sure.
{
motor(mtrb) = -40;
motor(mtrc) = 40;
}


Isn't that just a while statement, as the 'condition1' and 'condition3' phrases aren't used?!

Thanks, again, for all the answers!

You are correct. The two forms are identical. In this example, I've labeled the parts so you can see how to convert from one form to the other.
Code:
initializer;
while (condition) {
  body;
  increment;
}

// is exactly the same thing as

for (initializer; condition; increment) {
  body;
}

You should choose whichever form fits better with the given situation. If initializer and increment are blank, then while is usually a better choice.

There are a couple really important things to remember about using for() loops:
- If you declare a variable in the initializer, then the variable goes out of scope (doesn't exist) after the for exits. In the while() form, the variable still exists.
- The increment statement *always* executes after the body, even if the condition is now false. This is obvious in the while() form, but also true for the for().

Some examples:
Code:
int x = 0;
while (x < 1) {
  body;
  ++x;
}
// x still exists and has a value of 1

// declare y as part of the for()
for (int y = 0; y < 1; ++y) {
  body;
}
// y does not exist any more

// declare z before the for()
int z;
for (z = 0; z < 1; ++z) {
  body;
}
// z still exists and has a value of 1


But while that's all good to know, when I suggested you use PIDControl for your motors, I was thinking more along the lines of using nMotorEncoderTarget[]. You can learn more about this at http://www.education.rec.ri.cmu.edu/previews/robot_c_products/teaching_rc_lego_v2_preview/
Click on the "Movement" button at the top, then "Improved Movement" in the box on the left.

Have fun,
Terry

_________________
Vex Coach and robotics instructor


Thu Jul 17, 2014 2:03 pm
Profile
Rookie

Joined: Thu Mar 13, 2014 6:12 am
Posts: 18
Post Re: simplifying multiple 'if' statements
Not relating to our current topic, but on the website you talked about (https://www.securecoding.cert.org/confl ... sion+rules), it had the code example
Code:
char c1, c2;
c1 = c1 + c2;


Is this faster then individually assigning variables? e.g.
Code:
int examplevariable1 = 10;       //the individual assignment
int examplevariable2 = 24;

//compared to

int examplevariable1 = 10, examplevariable2 = 24;


Fri Jul 18, 2014 3:14 am
Profile
Display posts from previous:  Sort by  
Reply to topic   [ 10 posts ] 

Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  



Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group.
Designed by ST Software for PTF.