Selection Statement
Learning Objectives
At the end of this sub-unit, students should
- understand how selection statement works.
- know how to analyze selection statement.
- know how to write selection statement.
- know how to apply selection statement in problem solving.
Making a Choice
Let's look back at our solution for quadratic formula.
Quadratic Formula
Unfortunatelu, there is a slight problem with the formula above.
The expression (discriminant ** 0.5)
may produce no real number solution1.
This happens when the discriminant is negative.
We want to produce two different behavior depending on the sign of the discriminant.
- If the discriminant is negative, print
"No Real Solution"
. - Otherwise, print both \(x_1\) and \(x_2\) on separate lines.
Here we have a problem, we cannot do both. But based on what we have learnt so far, the execution will proceed from top to bottom, visiting every line. We need another construct to make choices such that we may execute some lines and not others.
We would highly encourage you to familiarize yourself with the basic terminology section of the appendix.
If-Statement
The construct to do that is called the if-statement. The general syntax can be described as follows.
- It must start with the
if
keyword.- The
if
keyword is followed by a conditional (i.e., boolean expression). - This line is terminated by a colon (i.e.,
:
). - The next line is an inner block called the then branch.
- The
- It can have 0 or more
elif
keyword2 in the same indentation level as theif
keyword.- The
elif
keyword is followed by a conditional (i.e., boolean expression). - This line is terminated by a colon (i.e.,
:
). - The next line is an inner block called the elif branch.
- The
- It can have 0 or 1
else
keyword in the same indentation level as theif
keyword.- The
else
keyword is NOT followed by a conditional (i.e., boolean expression). - This line is terminated by a colon (i.e.,
:
). - The next line is an inner block called the else branch.
- The
As a general rule, after every colon (i.e., :
), the next line should be more indented.
The reverse is also true.
Consider an indented line, there must be a line with fewer indentation that ends with a colon (i.e., :
).
To remember this, there is a notation we can use.
Syntax
A little explanation on the notation.
- Named surrounded by
❬❭
are not actual code but a placeholder for something else. - Elements surrounded
❲❳
are optional (i.e., they may or may not present).- Additionally, if they are then followed by
✱
, it means there can be 0 or more of them. - Alternatively --while not used here-- if they are then followed by
✚
, it means there can be 1 or more of them.
- Additionally, if they are then followed by
This match the explanation above.
This syntax and the explanation gives us some basic construction rule for selection statements which include the following.
The other variants are just varying the number of elif
.
Note that there cannot be 0 number of if
keyword to actually qualify as a selection statement.
Invalid Code
The following codes are invalid and will give syntax error.
Let us consider a simple if-statement and see how it is evaluated. The behavior is also called the semantics. In describing a new construct, we need to know the syntax and the semantics. Unfortunately, the mathematical description of the semantics is rather complicated. So, we will use an informal explanation for the semantics.
Semantics
When describing the semantics, we should not use the abstract syntax involving ❬❭
and ❲❳
.
Instead, we will use name as a placeholder for expressions, statements, and blocks.
The execution of if-statement above is as follows:
- Evaluate the boolean expression
cond1
into a value \(V_1\). - If \(V_1\) is
True
3, evaluate the blockblock1
and go to Step 6. - Otherwise (i.e., value is
False
), evaluate the boolean expressioncond2
into a value \(V_2\). - If \(V_2\) is
True
3, evaluate the blockblock2
and go to Step 6. - Otherwise (i.e., value is
False
), evaluate the blockcond3
and go to Step 6. - Evaluate the next statement (if any).
This behavior can be extended into multiple elif
by duplicating Step 3 and Step 4.
Note that we do the evaluation of the next condition only if the previous condition evaluates to False
.
On the other hand, if there is no elif
, then we simply skip Step 3 and Step 4.
Similarly, if there is no else
, then we simply skip Step 5.
Often, thinking about the semantics (i.e., the behavior) using text can be confusing. The more common explanation tool is the flowchart. In general, to trace a flowchart, you simply follow an arrow. However, there may be some elements with multiple arrow coming out. In such cases, you need to understand which path to take based on some condition. This is because the flowchart is a representation of the code and the code can only execute one line at a time. So it cannot have both arrows being executed at the same time.
Flowchart
The flowchart corresponding to the code above is shown below.
The arrow with the checkmark corresponds to the path taken when the corresponding condition (e.g., cond1
or cond2
) evaluates to True
.
The arrow with the crossmark corresponds to the path taken when the corresponding condition (e.g., cond1
or cond2
) evaluates to False
.
Concrete Example
Consider the problem of finding an absolute value of a floating point number in a variable x
and print the value.
If the value of x
is negative, we simply print -x
.
Otherwise, we print x
.
Converting this into code, we get the following code.
Absolute Value
The flowchart is shown below.
It is also interesting to see the execution step by step. We will illustrate that with our memory model. The path taken are highlighted. The memory model and the evaluation is given as comments.
The lines executed can be visualized below.
Back to Quadratic
Now that we have a better understanding of selection statement, we can look back at the quadratic formula problem and improve on it. First, let us recap the relevant information.
- If the discriminant is negative, print
"No Real Solution"
. - Otherwise, print both \(x_1\) and \(x_2\) on separate lines.
Translating this, we get the following code.
Quadratic Formula
Properties
It is worth noting some interesting properties of if-statement. We will illustrate this with the following generalized if-statement.
- At most one of the blocks will be executed.
- In other words, if
block3
is executed, no other blocks will be executed. - Note that if there is no
else
keyword, then it is possible that none of the blocks are executed.
- In other words, if
cond2
will only be evaluated ifcond1
evaluates toFalse
.- Similarly,
cond3
will only be evaluated ifcond1
andcond2
evaluates toFalse
. - In fact, if we order subsequence condition as
cond_i
, then it will only be evaluated if allcond_j
wherej < i
evaluates toFalse
.
- Similarly,
else_block
is only executed if all conditions evaluate toFalse
.
The second property may lead to an interesting behavior when multiple conditions may evaluate to True
.
Since the evaluation is still roughly top down, the top-most condition that evaluates to True
will have its corresponding block executed.
We say that this value satisfies the condition.
Afterwards, the execution jumps to the end of the if-statement.
Subsequent blocks will not be executed even if the corresponding condition will actually evaluate to True
if it ever reach.
Letter Grades
To illustrate the second property, considering the problem of assigning letter grades to marks according to the table below. Assume that all marks are integers between 0 and 100 (inclusive).
Mark \(M\) | Grade |
---|---|
\(90 \leq M \leq 100\) | A |
\(80 \leq M \leq 89\) | B |
\(70 \leq M \leq 79\) | C |
\(60 \leq M \leq 69\) | D |
\(0 \leq M \leq 59\) | F |
There are several possible solutions and we will show two interesting solutions below.
Letter Grade
In this solution, we have conditions that are overlapping. In other words, a given value may satisfy multiple conditions.
In this solution, we have conditions that are disjoint. In other words, a given value will only satisfy one condition.
Incorrect Letter Grade
Note that the order of the condition matters!
The following is incorrect as the latter conditions (e.g., mark >= 90
) is subsumed by the earlier conditions (e.g., mark >= 70
).
Analysis
Since there can be multiple correct solution, how can we analyze a particular solution involving if-statement? For one, we can test it with all marks from 0 to 100 and see if the answer are as expected. In this case, that will likely be the best way to test. Unfortunately, it may not work in general where the input can be unbounded.
We will show one possible way of analyzing if-statement that works for some numeric inputs. This is called a range analysis because we are only interested in the range of values a particular variable can take. Note that it is only an illustration. You may need a more imaginative technique to analyze your own code, but you may use this technique as a starting point.
We will indicate the range of values as follows.
[a, b]
means that the values are betweena
andb
(inclusive of both).[a, b)
means that the values are betweena
andb
(inclusive of a, exclusive of b).(a, b]
means that the values are betweena
andb
(exclusive of a, inclusive of b).(a, b)
means that the values are betweena
andb
(exclusive of a, exclusive of b).
Our analysis will use similar notation as our memory model. However, the value will be based on the range notation above. First, note that the correctness of the disjoint version is more obvious. In particular, it closely resembles the condition on the table. So, we only need to justify the overlapping version.
Try to understand the justification above slowly. This illustrates the kind of analysis you should try to make to check if your code is correct. Such an analysis can also be done while designing your solution using flowchart as shown below. At the very least, try to perform a simple analysis. This will help in finding problems with your code later on.
-
Luckily, Python can actually produce complex number, but we will not go into that. ↩
-
The
elif
keyword is a portmanteau ofelse
andif
.else
+if
⇝el
+se
if
⇝elif
. ↩ -
In Python, \(V_i\) need not evaluate exactly to
True
(i.e., boolean value). Instead, it only requires truthy/falsy values. However, it is a bad practice. ↩↩