CSCI 240 Lecture Notes - Part 3


Cascading if's

Sometimes the two alternatives provided by if aren't enough.

Suppose you need to decide if a student is a Freshman, Sophomore, etc. based on the number of credit hours.

There are more than two alternatives.

You can use this pattern - which is an extension of the simple if:

if (cond1)
  action1;
else if (cond2)
  action2;
else if (cond3)
  action3;
 

If none of cond1, cond2, and cond3 are true, no action is taken.

If you want some action to be taken when none of the conds are true, use this pattern:

if (cond1)
  action1;
else if (cond2)
  action2;
else if (cond3)
  action3;
else		// note no (cond) here
  action4;
 

action4 is done if and only if none of the conds is true.

In the first pattern, zero or one action is done. Never more than one.

In the second pattern, one and only one action is done.

So for the credit-hour - class standing problem, we could code the following:

int credits;
string classStanding;
 
cout<< "Enter number of credit hours: ";
cin >> credits;
 
if (credits >= 90)
  classStanding = "Senior";
else if (credits >= 60)
  classStanding = "Junior";
else if (credits >= 30)
  classStanding = "Sophomore";
else if (credits >= 0)
  classStanding = "Freshman";
else
  classStanding = "Error: ....";
 
cout << "\nClass is: " << classStanding; 

Conditional Expressions

Both looping and decision statements depend on a conditional expression:

if (i > 10)   while (i <= 10)	etc.	

(recall that for has a conditional expression embedded in it - the middle part)

A conditional expression is a logical expression that is either true or false.

The condition is enclosed in parenthesis (except in the for construct).

It usually uses a relational operator.

== (remember, two equal marks test for equality)
!= not equal
>  greater than
<  less than
>= greater than or equal
<= less than or equal
!> not greater than
!< not less than      etc.
 

(By the way, =>   =<   >!  will not compile.)

Numeric conditions are common:

if (sum == 0)
  cout << "no data entered; can't find average";
 
i = 1;
while (i < 10)
  {
  cout << endl << i;
  i++;
  }
 

Other kinds of conditional expressions are possible. For example, we will study more about functions that "become" true or false later.  And we have mentioned that in C++ integer values by themselves have a true or false meaning.  Specifically, 0 is false and anything else is true.  So it is legal to write:

if (n)
  doSomething;
else
  doSomethingElse;

Here, if n is 0 then "doSomethingElse" will be done; if n is non-zero, "doSomething" will be done.


Compound Conditions

Sometimes one condition is not enough.

Suppose we want to test if a point is inside a rectangle, and we know the x and y coordinates of the point; also we know the left, right, top, and bottom coordinates of the rectangle.  So we declare:

int ptX, ptY;
int rectLeft, rectRight, rectTop, rectBot;

Now assume all variables these variables have valid values.
Also, assume Cartesian coordinates.

Is the point in the rectangle?

Well,   if ptX is greater than rectLeft  AND
	if ptX is less than    rectRight AND
	if ptY is greater than rectBot AND
	if ptY is less than    rectTop
	THEN (meaning, if ALL of these are true...)
	the point is in the rectangle.
 

We can code this in C++, using && for the AND's (newer  C++ compilers also allow the word and):

if (ptX > rectLeft && ptX < rectRight && 
    ptY > rectBot  && ptY < rectTop)
  cout << "pt is in rect";
else
  cout << "pt is not in rect";
 

Note that we required that the point be inside the rectangle because we used > and <.

We could also allow the point to be on or inside the rectangle by using <= and >=.

How could we check to see if the point lies ON the top edge of the rectangle?

(Try it)

We can also code OR's of two conditions, using 2 vertical bars: || (again, newer compilers allow or):

Example: Suppose you want to ask if the user wants to continue:

cout << "Another? (y/n)";
cin >> choice;
 

But - you want to accept either "y" or "Y" to indicate "continue". (The user may have hit caps lock, or just carelessly hit shift).

do
  {
  ... some processing of one thing...
  cout << "Another? (y/n)";
  cin >> choice;
  }
while (choice == "y" || choice == "Y");
 

Another example: suppose your program is accepting a number representing a shoe size, and you know that shoe sizes cannot be smaller than 5 or greater than 18. Validate this number - that is, make the user type in a "valid" shoe size.  Use a compound condition:

cout << "Enter a shoe size";
cin >> size ;
if (size < 5 || size > 18)
  cout << "invalid size....";
 

You then put this in a loop, which will ask for a size until a valid one is entered:

Version 1:

cout << "Enter shoe size (5 to 18): "; //initial prompt
cin >> size;
while (size < 5 || size > 18)	       // OR not AND!!
  {
  cout << "Shoe size must be in range 5 to 18; try again: ";
  cin >> size;
  }
cout << "You entered " << size; 

This version needs two input lines, and uses two different messages. (The second only if a data entry error has been made.)

Sample script:

Enter the shoe size (5 to 18): 3
Shoe size must be in range 5 to 18; try again: 19
Shoe size must be in range 5 to 18; try again: 5
You entered 5

Note clearly the loop exit condition: you want to do the loop only if the user has entered a bad value.  So you code "while the size is bad - i.e. less than 5 or greater than 15"

If the initial value entered by the user is ok - say 10 - then the loop body won't execute at all because neither of the conditions is true.  10 is not less than 5; 10 is not greater than 18.  So both subconditions are false, so the whole thing is false, so the loop is not executed.

Don't code "while (size < 5 && size > 18)" because an invalid size would pass as valid:  Suppose the user types in 20.  20 < 5 is false. 20 > 15 is true.  Since both are not true the whole thing is false and so the loop body would not execute and so the invalid size would pass as valid.

Another way to understand it is simply: how could ANY size be both less than 5 and at the same time greater than 18?  It's impossible.  So ANY shoe size would create one false condition and so the loop body would never be executed for ANY shoe size, valid or invalid.

Clearly, compound conditions require careful thought.

Version 2:

do
  {
  cout << "Enter the shoe size (5 - 18): ";
  cin >> size;

  // Note changes to cond!!
  if (size >= 5 && size <= 18)	
    break;
  }
while (1);
cout << "You entered " << size;
 

Note the new condition.  Whereas before we wanted to execute the loop for an invalid value, now we want to break out of the loop when we have a valid value.

This version will work. But there's only one message. So user sees:

Enter the shoe size (5 - 18): 3
Enter the shoe size (5 - 18): 19
Enter the shoe size (5 - 18): 5
You entered 5

It's not 100% clear that the first two tries were wrong. We could put an else to the if and patch it to print out an error message. But Version 1 is probably better.


Combining && and ||

Truth tables:

(true  && true)  => true
(true  && false) => false
(false && true)  => false
(false && false) => false

So for (c1 && c2) to be true, both must be true.

(true  || true)  => true
(true  || false) => true
(false || true)  => true
(false || false) => false

So for (c1 || c2) to be true, one or the other or both must be true.

There are rules of evaluation for these operators:

  1. The evaluation looks at one condition at a time (i.e. cond1 op cond2)
  2. sub expressions inside () are done first
  3. && binds tighter than || (like * and / are done before + and -)
  4. otherwise, conditions are evaluated left to right
  5. as soon as the truth of the whole is known, evaluation stops
  6. but you can provide your own () to change the default rules (or to make the evaluation order clearer to the reader).

Suppose you have:

if (cond1 || cond2 && cond3)

The whole thing has to be true or false.

The parts are considered in pairs.

Now will C look at this as  (cond1 || cond2) && cond3
or as			     cond1 || (cond2 && cond3)
 

Because && precedes ||, the latter.  So

1. Evaluate cond1 (left-to-right rule).

2. If true, done.

3. Else (cond1 is false, so) evaluate (cond2 && cond3).  If both are true, the whole condition is true; otherwise it is false.

Example:

We want to judge num as valid if it is between 0 and 100 or if it is 2000.

if (num == 2000 || num >= 0 && num <= 100)
  cout << "valid";
else
  cout << "not valid";
 

1. Suppose num is 30.  Since num is NOT = 2000, we have to evaluate the second half of the condition. Since 30 is >=0 AND is <= 100, the && is satisfied and we have

if (false || true)

Since this is an OR and for an OR, the whole thing is true if either is true -  the whole thing is true.
 

2. Suppose num is 2000.  Since the first half is true, we don't have to evaluate the second half. We have:

if (true || anything)

Since this is an OR, it can be true if just one part is true, so the whole thing is true.

3. Suppose num is 150.  Since num is NOT = 2000, we have to evaluate the second half of the condition. 150 >= 0 BUT 150 is NOT <= 100.  So the && is not satisfied and we have:

if (false || false)

Since both halves are false, the whole thing is false.


Data type bool and flags

C++ defines a data type called bool. (Short for "boolean", from the name of a 19th century Irish logician, George Boole)

bool variables can have one of two possible values: true and false. (Note caps)

This data type comes in handy in writing programs that are easier to understand.

For example, consider the following partial example:

bool found = false;
while (!found)
  {
  //some complex code to search for something...
  //somewhere in it is the following:
  if (some condition is true)
    found = true;
  }
 

These kinds of variable that can be true or false are often called flags. They typically indicate if some event has happened yet or not, or as a category:

bool okToWithdraw;
double bal, request;
//get user's bank balance
// and store into bal
//get user's withdrawal request
// and store into request
if (bal - request > 0)
  okToWithdraw = true;
else
  okToWithdraw = false;
....
if (okToWithdraw)
  // give out money
else
  // print error message