Schedule‎ > ‎

05A Complex Decisions and Counting Loops

Questions, Answers and Review

  • Questions from last class?
  • Questions about labs or upcoming homework?

^ top

^ top

More About Selections

Learner Outcomes

At the end of the lesson the student will be able to:

  • Recognize the correct ordering of tests in multiple branches
  • Program conditions using Boolean operators and variables
  • Avoid some common pitfalls when creating test conditions

^ top

Multiple Alternatives

  • By using collections of if-else statements, a program can distinguish between multiple alternatives

Program For a HandshakeTester 

Image result for handshake cartoon

source:  123RF.com


void loop() {                     

  int pressure = analogRead(SENSOR);

  if (pressure < 100) {

    cout << “I can’t feel anything.“ << endl;

  }else if (pressure < 500) {

    cout << “Light grip“ << endl;

  }else if (pressure < 800) {

    cout << “Firm Grip“ << endl;

  }else{

     cout << "Ouch!!“  << endl;

  }

  delay(1000);

}


Choosing Between Alternatives

  • This program has four alternatives to choose from:
    1. I can't feel anything.
    2. Light grip.
    3. Firm grip.
    4. Ouch!!
  • Note that the order that the alternatives are checked is important
  • We can follow the alternatives in the flowchart shown below

Flowchart of Multiple Alternatives for Handshake Tester


Check Yourself

  1. In the above program, what happens if the pressure sensor reads 423?
  2. True or false? An elegant way to choose among multiple alternatives is to nest if statements in an else clause.
  3. If score = 85, what is output by the following code fragment?
  4. if (score >= 90)  {
        cout << "You got an A" << endl;
    }
    else if (score >= 80)  {
        cout << "You got a B" << endl;
    }
  5. In the above sequence of if-statements, the test condition of the first if-statement must be ________before the second if-statement executes.

^ top

4.2.2: When Order Matters

  • In some cases, the order of the tests is not important
    • IF all the tests used == then they can be done in any order
  • Note that in the love tester the order of the tests is important to ensure that the right results are printed

Order Is Important

  • If we rearranged the order of the if-else statements, we would get the wrong results
  • For example, if we reversed the order of the tests:
    if (pressure < 800) {
     cout << "Firm Grip" << endl;
    }else if (pressure < 500) {
    cout << "Light Grip" << end'
    }else if (pressure < 100) { cout << "I can't feel anything" << endl;
    }else{ cout << "Ouch!!" << endl; }
  • This does not work because Firm Grip will always be printed out.
  • Some tests will never be attempted

Importance of Using if-else-if Structure

  • Note that we cannot remove the else portion of the structure like shown below:
    if (pressure < 100) {
      cout << "I can't feel anything" << endl;
    }
    if (pressure < 500) {
      cout << "Light Grip" << endl;
    }
    
    if (pressure < 800) {
      cout << "Firm Grip" << endl;
    }
  • The conditions must be exclusive and we need the else-if conditions to ensure exclusivity
  • Independent if statements may cause a single input to print several messages

Check Yourself

  1. True or false? Order never matters in a sequence of if and else statements.
  2. Suppose the sensor read -1 into the love tester program. What is printed?
  3. How can the following code be simplified?
    if (price > 100) {
        discount = price - 20;
    } else if (price <= 100) {
        discount = price - 10;
    }
    

^ top

^ top

4.2.4: Nested Branches

  • Nested if-else statements can be used when there are two (or more) levels of decision making.
  • For example, consider the following two tax tables from 1992
  • There is a different table for each marital status (decision 1)
  • Once you have found the right table, you are taxed differently according to your income (decision 2)

Tax Table if Single

If your status is Single and if the taxable income is over but not over the tax is of the amount over
$0 $21,450 15% $0
$21,450 $51,900 $3,217.50 + 28% $21,450
$51,900
$11,743 + 31% $51,900

Tax Table if Married

If your status is Married and if the taxable income is over but not over the tax is of the amount over
$0 $35,800 15% $0
$35,800 $86,500 $5,370.00 + 28% $35,800
$86,500
$19,566.00+ 31% $86,500

Programming Two-Level Decisions

  • When we program this two-level decision process, we often use two levels of if statements
  • We say the income test is nested inside the test for filing status
  • We can see this two-level decision in the flowchart shown below
  • Also, we can examine and run the program from the code shown below
  • Note that more complicated decisions may require deeper levels of nesting

Flowchart of Two-level Tax Decision Process

Develop a Flowchart of the Two-level Tax Decision Process in Teams on WhiteBoards

Program Computing Single and Married Tax Rates

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
void setup() {
Serial.begin(9600);
}
void loop() {
const double SINGLE_LEVEL1 = 21450.00;
const double SINGLE_LEVEL2 = 51900.00;

const double SINGLE_TAX1 = 3217.50;
const double SINGLE_TAX2 = 11743.50;

const double MARRIED_LEVEL1 = 35800.00;
const double MARRIED_LEVEL2 = 86500.00;

const double MARRIED_TAX1 = 5370.00;
const double MARRIED_TAX2 = 19566.00;

const double RATE1 = 0.15;
const double RATE2 = 0.28;
const double RATE3 = 0.31;

int marital_status;
double income;
double tax;

cout << "What is your income?" << endl;
cin >> income;
while (Serial.available()) { //put this after your cin statement
Serial.read();
}
cout << "Please enter 1 for single, 2 for married: " << endl;
cin >> marital_status;
while (Serial.available()) { //put this after your cin statement
Serial.read();
}

if (marital_status == 1) {
if (income <= SINGLE_LEVEL1) {
tax = RATE1 * income;
} else if (income <= SINGLE_LEVEL2) {
tax = SINGLE_TAX1 + RATE2 * (income - SINGLE_LEVEL1);
} else {
tax = SINGLE_TAX2 + RATE3 * (income - SINGLE_LEVEL2);
}
} else {
if (income <= MARRIED_LEVEL1) {
tax = RATE1 * income;
} else if (income <= MARRIED_LEVEL2) {
tax = MARRIED_TAX1 + RATE2 * (income - MARRIED_LEVEL1);
} else {
tax = MARRIED_TAX2 + RATE3 * (income - MARRIED_LEVEL2);
}
}
cout << "The tax is $";
cout << tax << endl;
}

Try It: Using Nested if-statements (8m)

We want to write a program to calculate a student's letter grade according to the following table:

Numerical Grade Letter Grade
greater than or equal to 90 A
less than 90 but greater than or equal to 80 B
less than 80 but greater than or equal to 70 C
less than 70 but greater than or equal to 60 D
less than 60 F
  1. Copy the following program into the Arduino IDE editor and then compile and run the starter program to make sure you copied it correctly.
    void setup() {
      Serial.begin(9600);
    }
    
    void loop() {
    }
  2. Add code to get user input into a variable named score of type double. When you run the program after adding this code, the output should look like:
    Enter a score: 95.7
    

    Make sure you declare the variable with a compatible data type. Note that the underlined numbers above shows what the user enters and is not part of your code. For more information see section 2.4.7: Input and Output.

  3. First we will look at a series of if statements and see that if statements alone are not enough to solve this problem. Copy the following into your program after the input statements:
    String grade;
    if (score >= 90) {
        grade = "A";
    }
    if (score >= 80) {
        grade = "B";
    }
    if (score >= 70) {
        grade = "C";
    }
    if (score >= 60) {
        grade = "D";
    }
    if (score < 60) {
        grade = "F";
    }
    Serial.println(grade);    //must use Serial.println because grade is a String
    

    Compile and run your modified program. There is a logic problem with this code. Each test condition needs to work over a range of values rather than with a single value.

  4. One way to correct the problem is to nest an if statement inside of another if statement. To see how this works, modify your code to add nested if statements as shown below:
  5. To test the range, the outer if statement tests the lower condition and the inner if statement tests the upper condition.

  6. Compile and run your modified program to make sure you made the changes correctly. When you run the program, the output should look like:
    Enter a score: 80
    B
    

    Run your program a few times with different score to verify that any score displays the correct letter grade.

  7. Save your grader.ino file because you will use it soon.
  8. Be prepared to answer the following Check Yourself questions when called upon.

Check Yourself

  1. True or false? You can nest if statements within another if statement.
  2. If you are single and your taxable income is $21,450, your tax is ________.
  3. If you get a $1000 per year raise and now make $22,450, you now pay taxes of ________.
  4. Some people object to higher taxes for higher incomes, claiming that you might end up with less money after taxes when you get a raise for working hard. The flaw in this argument is ________.

^ top

4.2.5: Boolean Variables

  • Sometime we need to evaluate a logical condition in one part of a program and use it elsewhere
  • To store a condition that can only be true or false, we use a Boolean variable
  • Boolean variables are named after George Boole (1815-1864), a pioneer in the study of logic
  • We specify a Boolean variable using the bool type, which can hold just one of two values: true orfalse
    bool isCool = true;
    bool lies = false;
    
  • Question: What type of tests does George Boole give? (answer)
  • Question: How does George Boole order lunch? (see here)

Test Conditions and Boolean Values

  • Remember that test conditions always evaluate to true or false
    if (num > 0)
    
  • Thus we can use a boolean variable as a test condition
    bool isPositive = (num >= 0);
    if (isPositive)
    
  • Note that we do not need to add a relational expression to a boolean variable, like:
    if (isPositive == true) // avoid!
  • Since the boolean variable already evaluates to true or false, adding the == true is redundant
  • Likewise, we do not need to use:
    if (isPositive != false) // avoid!
  • If we want to reverse the test condition, we can use the not (!) operator
    if (!isPositive)
  • We can see the use of a Boolean variable in the following example

Example Application Using a Boolean Variable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

void setup() {
Serial.begin(9600);
}

void loop() {
double num;
cout << "Enter a number:" << endl;
cin >> num;

bool isPositive = (num >= 0);
cout << "The test evaluated to: " << isPositive << endl;
if (isPositive) {
cout << "The number was 0 or positive" << endl;
} else {
cout << "The number was negative" << endl;
}
}

Check Yourself

  1. True or false: the number of values that can be stored in a Boolean data type are 3: true, false and null.
  2. True or false: the Boolean data type is spelled boolean (with a lower case "b") in C++.
  3. True or false: a test condition always evaluates to a Boolean value.
  4. True or false: a Boolean variable can substitute for a test condition in an if-statement.

^ top

4.2.6: Logical Operators

  • Sometimes we need to test for multiple conditions in our programs
  • For example, we want to test if an age is between 18 and 25
  • We need to test both that age >= 18 and age <= 25
  • One way to make the tests is with nested if statements
    int age = 0;
    cout << "Enter your age:" << endl;
    cin >> age;
    if (age >= 18)
    {
        if (age <= 25)
        {
            cout << "Correct age!" << endl;
        } else {
            cout << "Wrong age!" << endl;
        }
    } else {
        cout << "Wrong age!" << endl;
    }
    
  • If the age entered is correct, like 19, we get the message, "Correct age!"
  • If the age entered is not correct, like 15, we get the message, "Wrong age!"
  • While this works, it is cumbersome to code and read
  • A better approach is to combine test conditions with logical operators

Combining Test Conditions with Logical Operators

  • logical operator, or Boolean operator, is an operator that treats operands as Boolean values (true orfalse)
  • C++ has several logical operators, but we only need to use three to create any possible test condition
  • These three operators are andor and not, which are discussed below
  • These logical operators are traditionally written as && (and), || (or) and ! (not)
  • Both variants are legal under ANSI C++
  • The words are easier to read but many C++ programmers still use the older form

Truth Tables for and, or and not

and (&&) Operator Truth Table
If expr1 is... And expr2 is... Then expr1 and expr2 is... Example Result
true true true 5 < 10 and 5 > 2 true
true false false 5 < 10 and 5 < 2 false
false true false 5 > 10 and 5 > 2 false
false false false 5 > 10 and 5 < 2 false
or (||) Operator Truth Table
If expr1 is... || expr2 is... Then expr1 or expr2 is... Example Result
true true true 5 < 10 or 5 > 2 true
true false true 5 < 10 or 5 < 2 true
false true true 5 > 10 or 5 > 2 true
false false false 5 > 10 or 5 < 2 false
not (!) Operator Truth Table
If expr is... Then ! expr is... Example Result
true false !true false
false true !(5 < 2) true

Example Using Logical Operators

  • We could rewrite our age test using an and (&&) operator like this:
    int age = 0;
    cout << "Enter your age:" << endl;
    cin >> age;
    if (age >= 18 && age <= 25)
    {
        cout << "Correct age!\n";
    } else {
        cout << "Wrong age!\n";
    }
  • Notice that the code is shorter and it is easier to follow the logic
  • Another way to use logical operators to test the age is:
    int age = 0;
    cout << "Enter your age:" << endl;
    cin >> age;
    if (age < 18 || age > 25)
    {
        cout << "Correct age!\n";
    } else {
        cout << "Wrong age!\n";
    }
  • Many people confuse && and || conditions, especially when learning about logical operators
  • A value lies between 0 and 100 if the value is at least 0 and at most 100
  • A value is outside that range if it is less than 0 or greater than 100
  • There is no golden rule; we have to think carefully and test our conditions

Another Look at Truth Tables

  • Note that most computers store true as 1 and false as 0
  • The NOT operator simply reverses its operand

Parenthesis

  • Remember that a Boolean expression in an if statement must be enclosed in parenthesis
  • Thus, an if statement with && might look like:
    if ((guess != GUESS1) && (guess != GUESS2))
  • However, relational operators have a higher precedence than logical operators
  • Thus, we can remove the inner parenthesis without affecting the meaning:
    if (guess != GUESS1 && guess != GUESS2)
  • However, if using parenthesis is easier to understand then use the extra parenthesis

Example Program with Logical Operators

1
2
3
4
5
6
7
8
9
10
11
12
13
14

void setup() {
  Serial.begin(9600); 
}

void loop() {
  bool op1 = false, op2 = false;
cout << "Enter true(1) or false(0) for two operands: " << endl;
cin >> op1;
cin >> op2;
bool answer1 = op1 && op2;
bool answer2 = op1 || op2;
cout << op1 << " and " << op2 << " = " << answer1 << endl;
cout << op1 << " or " << op2 << " = " << answer2 << endl; }

Try It: Using Logical Operators (5m)

We previously used a nested if-statement to test multiple conditions. Often, a better approach is to use Boolean operators like && and ||.

  1. Modify your grader.ino code from the last Try It to remove nested if statements and replace them with logical operators.
  2. Compile and run your modified program to make sure you made the changes correctly. When you run the program, the output should look like:
    Enter a score: 80
    B
    

    Run your program a few times with different score to verify that any score displays the correct letter grade.

  3. Save your grader.ino file to submit as part of the next exercise.
  4. Be prepared to answer the following Check Yourself questions when called upon.

More Information

Check Yourself

  1. Of the following groups ________ is larger.
    1. Students wearing denim
    2. Students wearing denim AND corrective lenses
  2. Of the following groups ________ is larger.
    1. Students wearing denim
    2. Students wearing denim OR corrective lenses
  3. Of the following groups ________ is larger.
    1. Students wearing denim
    2. Students wearing denim AND NOT corrective lenses
  4. For the following code, the test condition evaluates to ________.
    bool denim = true;
    bool lenses = false;
    Serial.println(denim && lenses);
    
  5. For the following code, the test condition evaluates to ________.
    int age = 21;
    Serial.println(age >= 18 && age <= 25);
    
  6. Of the following logical expressions, the test to see if x is between 1 and 10 (including 1 and 10) is________.
    1. (x >= 1 && x <= 10)
    2. (1 <= x and x <= 10)
    3. (x >= 1 || x <= 10)
    4. (1 <= x or x <= 10)

^ top

Exercise 1 Multiple Alternatives (grader.ino)

In this exercise we test multiple alternatives in a program. As an example, we will calculate a student's letter grade according to the following table:

Numerical Grade Letter Grade
greater than or equal to 90 A
less than 90 but greater than or equal to 80 B
less than 80 but greater than or equal to 70 C
less than 70 but greater than or equal to 60 D
less than 60 F

Specifications

  1. Complete the Try It: Using Nested if-statements if  you  haven't already.
  2. In addition, complete the Try It: Using Logical Operators.  Before going on to step 3,  put step 1 & 2 into a comment block /*   */
  3. Perhaps the most elegant solution is to nest an if statement in the else clause of the preceding if. Modify the series of if statements in grader.ino to include an else clause.

    We are nesting if statements in the else clause. Nesting in the else clause makes each test condition of the if statement exclusive of the others because each test condition eliminates all the preceding conditions. Thus, in this scenario the order is important. For more information see section 4.2.2: When Order Matters.

  4. Compile and run your modified program to make sure you made the changes correctly. When you run the program, the output should look like:
    Enter a score: 80
    B
    

    Run your program a few times with different scores to verify that any score displays the correct letter grade.

  5. Submit your final program source code  grader.ino to Canvas.
^ top

4.2.7: Conditional Pitfalls

  • Unfortunately, you can write many things in C++ that should be incorrect but end up working for some obscure reason
  • This means that you can code something that should create an error message but does not
  • Thus, a program may compile and run with no error messages but still be wrong
  • Since you may not realize that it is wrong, it can be hard to find and correct these types of errors

Using = Instead of ==

  • One common mistake is to use = when you meant to use ==
  • For example, look at the test condition in the following code:
    if (guess = 7) {
        Serial.println("*** Correct! ***");
    } else {
        Serial.println("Sorry, that is not correct.");
    }
    
  • Notice that the condition is really an assignment statement and not a test
  • You would think that it would fail to compile -- but it does not
  • However, it will not work as you might expect
  • A way to prevent this type of problem is to reverse the order of your test condition:
    if (7 = guess) {
  • Now the compiler will give you an error message and your code will not compile:
    guess.ino: In function `void loop()":
    guess:10: error: non-lvalue in assignment
    
  • However, if you correctly use == then your code will compile
    if (7 == guess) {

Strings of Inequalities

  • Do NOT use a string of inequalities like the following:
    int a = 5, b = 1, c = 10;
    if (a < b < c) {
        cout << "b is between a and c" << endl;
    } else {
        cout << "b is NOT between a and c" << endl;
    }
    
  • Your code may compile and run but give incorrect results
  • The test condition is evaluated by the computer from left to right
  • The first condition is a < b which evaluates to 0 (false)
  • The second condition is then 0 < c which evaluates to 1 (true)
  • Since the whole test condition evaluates to true you get an incorrect result
  • Instead, the correct way is to use && as follows:
    int a = 5, b = 1, c = 10;
    if (a < b && b < c) {
        cout << "b is between a and c" << endl;
    } else {
        cout << "b is NOT between a and c" << endl;
    }
    

Strings of Logical Operators

  • Logical expressions often read like "normal" English.
  • However, C++ requires more exactness than English
  • For example, the following code will compile and run but give wrong results:
    int guess;
    cout << "Enter a guess:" << endl;
    cin >> guess;
    if (guess == 7 || 8) {
        cout << "*** Correct! ***" << endl;
    } else {
        cout << "Sorry, that is not correct." << endl;
    }
    
  • The test condition is evaluated by the computer from left to right
  • The left hand side is (guess == 7) which can evaluate to either true or false
  • The right hand side is 8, which is interpreted as true by C++
  • Since (something or true) is always true, then the test condition always evaluates to true
  • Instead, the correct way is to use || as follows:
    int guess;
    cout << "Enter a guess:" << endl;
    cin >> guess;
    if (guess == 7 || guess == 8) { cout << "*** Correct! ***" << endl; } else { cout << "Sorry, that is not correct." << endl; }

Check Yourself

  1. Can you spot the error in the following code fragment?
    if (person = terrorist) {
        punish_severely();
    } else {
        return 0;
    }
    

    Answer and credit for the idea.

  2. What is wrong with the following string of inequalities and how do you correct the code?
    int a = 5, b = 1, c = 10;
    if (a < b < c) {
        Serial.println("b is between a and c");
    } else {
        Serial.println("b is NOT between a and c");
    }
    
  3. What is wrong with the following string of logical operators and how do you correct the code?
    int guess;
    Serial.println("Enter a guess:");
    guess = Serial.parseInt(); if (guess == 7 || 8) { Serial.println("*** Correct! ***"); } else { Serial.println("Sorry, that is not correct."); }

^ top

Summary

  • By using collections of if-else statements, a program can distinguish between multiple alternatives
  • Sometimes the order of statements is important for our program to work correctly
  • We must think carefully and test our conditions rigorously
  • Nested if-else statements can be used when there are two (or more) levels of decision making.
  • We looked at an example of tax tables, filing as single or married
  • To create conditions with multiple cases, we looked at using logical operators: &&|| and !
  • We looked at some examples including:
    ((guess != GUESS1) && (guess != GUESS2))
    ((GUESS1 == guess) || (GUESS2 == guess))
    !((GUESS1 == guess) || (GUESS2 == guess))
    
  • Unfortunately, you can write things in C++ that should be incorrect but end up working for some obscure reason
  • These types of errors are often very difficult to find
  • One common mistake is to use = when you mean to use ==
    if (guess = 7)
  • Which should be written as:
    if (guess == 7)
  • Another common problem is trying to use a string of inequalities without a logical operator separating each condition, like:
    if (a < b < c)
  • Which should be written as:
    if (a < b && b < c)
  • Another common error is trying to use logical operators without enough operands:
    if (guess == 7 || 8)
  • Which should be written as:
    if (guess == 7 || guess == 8)

Check Yourself

Answer these questions to check your understanding. You can find more information by following the links after the question.

  1. True or false? An elegant way to choose among multiple alternatives is to nest if statements in an else clause. (4.2.1)
  2. If score = 85, what is output by the following code fragment? (4.2.1) 
  3. True or false? Order never matters in a sequence of if and else statements. (4.2.2)
  4. In a game program, the scores of player A and B are stored in variables scoreA and scoreB. Assuming that the player with a larger score wins, write a sequence of conditional statements that prints our "A won", "B won" or "Game tied". (4.2.2)
  5. What is the difference between if-else if-else and nested if statements? (4.2.2 vs. 4.2.4, answer)
  6. True or false? A switch statement is a more powerful solution to multiple if-else if-else statements (4.2.3)
  7. True or false? You can nest if statements within another if statement. (4.2.4)
  8. What date type stores only the values true or false? (4.2.5)
  9. When does an AND (&&) of two or more conditions evaluate to true? (4.2.6)
  10. When does an OR (||) of two or more conditions evaluate to false? (4.2.6)
  11. What is the effect of the NOT (!) operator? (4.2.5)
  12. True or false? A && B is the same as B && A for any Boolean conditions A and B. (4.2.6)
  13. If score = 85, what is output by the following code fragment? (4.2.6)
  14. How many errors can you spot in the following code fragment? (4.2.7)
    if (person = terrorist) {
        punish_severely();
    } else {
        return 0;
    }
    

    Answerand credit for the idea.

  15. What is wrong with the following string of inequalities and how do you correct the code? (4.2.7)
    int a = 5, b = 1, c = 10;
    if (a < b < c) {
        Serial.println("b is between a and c");
    } else {
        Serial.println("b is NOT between a and c";
    }
    
  16. What is wrong with the following string of logical operators and how do you correct the code? (4.2.7)
    int guess;
    Serial.println("Enter a guess:");
    guess = Serial.parseInt();
    if (guess == 7 || 8) {
        cout << "*** Correct! ***\n";
    } else {
        cout << "Sorry, that is not correct.\n";
    }




Source: www.arduino.cc

^ top

Wrap Up and Reminders

  • For the next homework, see the schedule
  • When class is over, please shut down your computer.
  • Complete unfinished exercises from today before the next class meeting


^ top