At the end of the lesson the student will be able to:
Know what was missed on the midterm
Decide on what you need to do to improve your performance, if anything
^ top
Post Midterm Review
Has anyone NOT completed the midterm yet?
Post midterm data
If you had problems with a topic I suggest reviewing the notes, homeworks and labs
We will have another midterm before the final
Grading policies, including midterm weighting, are listed in the syllabus
^ top
Post Midterm Survey
^ top
Making the Grade
Oftentimes students who got less than an "A" on the midterm wonder if they can still get an "A" in the course
Since this is the first midterm, the answer is "Yes" unless:
You have not completed most of the homework
You failed the midterm with < 50%
Even with the above, you can still pass the course and even get a "B"
However, you will need to start doing more homework and study for the tests
^ top
Exercise: Midterm Self-Reflection
In this exercise we reflect on our progress in this course.
You Can Learn Anything: from the Khan Academy
Specifications (5m)
Please take out a piece of paper and something to write with.
There is no need to tear the paper out of a notebook. If you need paper or a writing tool, ask a classmate or the instructor for one.
Reflect on the following questions and write down an answer for yourself:
What do you want to get from this course?
Are you achieving your goals for this course?
What ways can you think of to improve your performance in achieving your goal?
What actions are you going to take to better meet your goals?
^ 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
For example, consider the following example where a user enters the name of a coin, and the program displays the value
Program For an Arcade Love Tester
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void loop() {
int pressure = analogRead(SENSOR);
if (pressure < 100) {
Serial.println("Please hold my hand.");
}else if (pressure < 500) {
Serial.println("Just friends.");
}else if (pressure < 800) {
Serial.println("In love.");
}else{
Serial.println("Ouch!!");
}
delay(1000);
}
Choosing Between Alternatives
This program has four alternatives to choose from:
Please hold my hand.
Just friends.
In love.
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 Alternative for Love Tester
In the above program, what happens if the pressure sensor reads 423?
True or false? An elegant way to choose among multiple alternatives is to nest if statements in an else clause.
If score = 85, what is output by the following code fragment?
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) { Serial.println("In love.");
}else if (pressure < 500) { Serial.println("Just friends."); }else if (pressure < 100) { Serial.println("Please hold my hand.");
}else{ Serial.println("Ouch!!");
}
This does not work because all "Just Friends" values also meet the "In love" criteria
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) { Serial.println("Please hold my hand."); }
if (pressure < 500) { Serial.println("Just friends.");
} if (pressure < 800) {
Serial.println("In love.");
}
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
True or false? Order never matters in a sequence of if and else statements.
Suppose the sensor read -1 into the love tester program. What is printed?
How can the following code be simplified?
if (price > 100) {
discount = price - 20;
} else if (price <= 100) {
discount = price - 10;
}
^ top
4.2.3: switch Statements
The switch statement provides an alternative to an if-else-if chain
Executes a section of code depending on the value of a single number
The general syntax is:
switch (integerExpression) { case label1: statements break; case label2: statements break; ... case labeln: statements break; default: statements
}
Where:
integerExpression: an arithmetic expression that resolves to an integer number
labelx: an integer constant
statements: the statements to execute when the condition is met
Any number of case labels can be placed in any order
When run, the integerExpression tries to match one of the case labels
If a label matches then the statements after the case label start executing
Any value that does not match starts executing with the statement after default
Execution continues until the end of the switch statement or encountering a break statement
The break statement causes an immediate exit from the switch statement
Just as case identifies possible starting points, break determines end points
void setup() { Serial.begin(9600);
} void loop() { int c = Serial.read(); switch (c) { case -1: // Nothing read, do nothing! break; case 'a': Serial.println("Apple"); break; case 'b': Serial.println("Bananna"); break; case '\r': case '\n': Serial.println("Ignoring newline characters..."); break; default: Serial.println("Some other fruit!"); }
}
When to Use switch Statements
Switch statements can only be used when
The are integers or chars
You are looking for exact matches
Thus, switch statements are inherently less useful than if-else statements
Also, the syntax is no clearer than if-else statements
There is a reason for the limitations of the switch statement
The compiler can generate faster code for switch statements
Though you shouldn't count on it...
However, modern compilers are quite capable of optimizing if-else statements
Be careful when using a switch statement
Every branch of the switch statement must be terminated by a break statement
If the break statement is missing, the program falls through and executes the next case without testing
There are rare uses for this fall through behavior, such as printing the words for the song, The Twelve Days of Christmas
However, according to a study by Peter van der Linden, reported in his book, Expert C Programming, p. 38, the falling through behavior is needed less than 3% of the time
Thus, the default behavior is wrong 97% of the time
Forgetting to type the break statement is a very common error and the source of many bugs
So one has to ask oneself, "Why use an inferior programming statement that causes more problems than it solves?"
Check Yourself
True or false: C++ switch statements allow testing for inequalities like < and >.
True or false: the default behavior of a switch statement, to continue executing past the next label, is usually correct.
True or false: programmers never forget to include a break statement when needed inside a switchstatement.
^ 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
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
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); Serial.setTimeout(-1);
} void loop() {
}
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.
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);
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.
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:
To test the range, the outer if statement tests the lower condition and the inner if statement tests the upper condition.
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.
Save your grader.ino file to submit as part of the next exercise.
Be prepared to answer the following Check Yourself questions when called upon.
Check Yourself
True or false? You can nest if statements within another if statement.
If you are single and your taxable income is $21,450, your tax is ________.
If you get a $1000 per year raise and now make $22,450, you now pay taxes of ________.
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
void setup() { Serial.begin(9600); Serial.setTimeout(-1);
} void loop() { double num; Serial.println("Enter a number:"); num = Serial.parseFloat(); bool isPositive = (num >= 0); Serial.print("The test evaluated to: "); Serial.println(isPositive); if (isPositive) { Serial.println("The number was 0 or positive"); }else{ Serial.println("The number was negative"); } Serial.println();
}
Check Yourself
True or false: the number of values that can be stored in a Boolean data type are 3: true, false and null.
True or false: the Boolean data type is spelled boolean (with a lower case "b") in C++.
True or false: a test condition always evaluates to a Boolean value.
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;
Serial.println("Enter your age:");
age = Serial.parseInt();
if (age >= 18)
{ if (age <= 25) { Serial.println("Correct age!"); } else { Serial.println("Wrong age!"); }
} else { Serial.println("Wrong age!");
}
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
A 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 and, or 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;
Serial.println("Enter your age:");
age = Serial.parseInt();
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;
Serial.println("Enter your age:");
age = Serial.parseInt();
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
If we substitute 1 for true and 0 for false, we have these truth tables:
With this substitution we see that the AND operation is the minimum of the operands
Conversely, the OR operation is the maximum of the operands
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
void setup() { Serial.begin(9600); Serial.setTimeout(-1);
} void loop() { bool op1 = false, op2 = false; Serial.println("Enter true(1) or false(0) for two operands: "); op1 = Serial.parseInt(); op2 = Serial.parseInt(); Serial.print(op1); Serial.print(" and "); Serial.print(op2); Serial.print(" = "); Serial.println(op1 and op2); Serial.print(op1); Serial.print(" or "); Serial.print(op2); Serial.print(" = "); Serial.println(op1 or op2); Serial.println();
}
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 ||.
Modify your grader.ino code from the last Try It to remove nested if statements and replace them with logical operators.
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.
Save your grader.ino file to submit as part of the next exercise.
Be prepared to answer the following Check Yourself questions when called upon.
More Information
Check Yourself
Of the following groups ________ is larger.
Students wearing denim
Students wearing denim AND corrective lenses
Of the following groups ________ is larger.
Students wearing denim
Students wearing denim OR corrective lenses
Of the following groups ________ is larger.
Students wearing denim
Students wearing denim AND NOT corrective lenses
For the following code, the test condition evaluates to ________.
bool denim = true;
bool lenses = false;
Serial.println(denim && lenses);
For the following code, the test condition evaluates to ________.
int age = 21;
Serial.println(age >= 18 && age <= 25);
Of the following logical expressions, the test to see if x is between 1 and 10 (including 1 and 10) is________.
(x >= 1 && x <= 10)
(1 <= x and x <= 10)
(x >= 1 || x <= 10)
(1 <= x or x <= 10)
^ top
Exercise 4.2 (5m)
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
Complete the Try It: Using Nested if-statements.
In addition, complete the Try It: Using Logical Operators.
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.
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.
Submit your final program source code to Canvas as part of lab 6.
As time permits, read the following sections and be prepared to answer the Check Yourself questions in the section: 4.2.8: Summary.
^ 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) { Serial.println ("b is between a and c");
} else { Serial.println ("b is NOT between a and c");
}
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) { Serial.println("b is between a and c");
} else { Serial.println("b is NOT between a and c");
}
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;
Serial.println("Enter a guess:");
guess = Serial.parseInt();
if (guess == 7 || 8) { Serial.println("*** Correct! ***");
} else { Serial.println("Sorry, that is not correct.");
}
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;
Serial.println("Enter a guess:");
guess = Serial.parseInt(); if (guess == 7 || guess == 8) { Serial.println("*** Correct! ***");
} else { Serial.println("Sorry, that is not correct.");
}
Check Yourself
Can you spot the error in the following code fragment?
if (person = terrorist) { punish_severely();
} else { return 0;
}
Answer and credit for the idea.
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");
}
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.
True or false? An elegant way to choose among multiple alternatives is to nest if statements in an else clause. (4.2.1)
If score = 85, what is output by the following code fragment? (4.2.1)
True or false? Order never matters in a sequence of if and else statements. (4.2.2)
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)
What is the difference between if-else if-else and nested if statements? (4.2.2 vs. 4.2.4, answer)
True or false? A switch statement is a more powerful solution to multiple if-else if-else statements (4.2.3)
True or false? You can nest if statements within another if statement. (4.2.4)
What date type stores only the values true or false? (4.2.5)
When does an AND (&&) of two or more conditions evaluate to true? (4.2.6)
When does an OR (||) of two or more conditions evaluate to false? (4.2.6)
What is the effect of the NOT (!) operator? (4.2.5)
True or false? A && B is the same as B && A for any Boolean conditions A and B. (4.2.6)
If score = 85, what is output by the following code fragment? (4.2.6)
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.
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";
}
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";
}
^ 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