Chapter 3 Conditionals

In the previous chapter, we looked at some very simple Python programs. In those programs, we observed how they were executed line-by-line sequentially: the first line ran, then the second line, and so on, until we reached the end of the program. However, we will often want a program that is more flexible than a sequence of steps. In particular, we may want the program to execute different lines of code depending on certain circumstances. For example, when we go to the ATM, there is a program running on the machine that first asks us to enter our details, and then asks what we want to do. Depending on our response, the program will display different things. If we want to withdraw money, it will ask how much to withdraw, but if we want to check our balance, it will simply display the amount. The ability to control the order in which statements execute is known by many names (conditionals, branching, if statements) and is what we’ll cover in this chapter.

3.1 Logical Expressions

Before we get into that, let’s look at one of the fundamental data types we mentioned in the previous chapter: the Boolean. As we briefly touched on, a Boolean variable is a variable that can take one of two values: TRUE or FALSE. Booleans are used to therefore determine whether an expression is true or not. Such expressions are known as logical expressions and they differ from the arithmetic expressions we have previously seen. For example, we would evaluate the arithmetic expression 2 * 3 as 6, but how do we evaluate the expression 2 > 3? Clearly when we evaluate this, we do not get a number! Instead, we get a statement about its truth, which in this case is false: two is not greater than three.

3.2 Logical Operations

Just as arithmetic expressions contain terms with operators like addition and multiplication, so too do logical expressions. These logical operators can be applied to Boolean variables to modify their values, or can be applied to other variables to produce Boolean results. For example, we can apply the greater (>) operator to two numbers to produce a Boolean outcome.

There are three fundamental Boolean operators (from which other operators can be derived). These are:

  1. Negation: negating a Boolean variable flips its value, so true becomes false and false becomes true
  2. Or/Union/Disjunction: generally known as the OR operator, this is applied to two Boolean variables. The result is true if at least one of the variables is true, and false otherwise.
  3. And/Intersection/Conjunction: generally known as the AND operator, this is applied to two Boolean variables. The result is true if both of the variables are true. If either or both are false, the result is false.

Question! Using the above rules for logical operators, evaluate whether the following statements are true or false:

  1. 4 is a positive number
  2. 4 is a positive number OR -1 is a positive number
  3. 4 is a positive number AND 3 is a positive number
  4. 4 is a positive number AND -1 is a positive number
  

Boolean variables can be represented numerically. By convention, False has a numeric value of \(0\), and True has a numeric value of any non-zero integer (but usually \(1\)). The tables below are known as truth tables, and you will encounter them in BCO. They are used to list all the possible truth values of a set of variables, as well as the outcome of applying Boolean operators to them. If we have two logical expressions A and B, then the truth table for the three fundamental operators are as follows:

A B not A A and B A or B
False False True False False
False True True False True
True False False False True
True True False True True

Because we can represent them numerically as well, we can write the truth tables using 1s and 0s instead:

A B not A A and B A or B
0 0 1 0 0
0 1 1 0 1
1 0 0 0 1
1 1 0 1 1

Question! Using the above table, can you see the mathematical operators that produce the output of and, or, and not respectively? To start you off, the not operation can be written as \(f(x) = 1 - x\).

Boolean algebra side quest! In the above question, we could almost say that the or operation is like addition, but it’s not quite because \(1 + 1 = 2 \neq 1\). Because we are missing an addition operation, the above logical system is known as a monoid. However, if we wanted to integrate the above into larger systems to develop an entire algebra based on Boolean variables, then we really need an addition operator. Is there a Boolean operator that would satisfy addition? What is it?

3.2.1 Order of Precedence

Just as with our regular operators, Boolean operators are evaluated according to their rules of precedence, described in the table below. Importantly, all the Boolean operators (except brackets) have lower precedence than the arithmetic operators. Hence, we evaluate all arithmetic operators first (e.g. addition and multiplication) and only then do we evaluate Boolean operators (e.g. equality and logical not).

Precedence Level Operator/Symbol Operation Evaluation Direction
0 (first) () parentheses Inner ones evaluated first. Left-to-right
1 \(<, \le, >, \ge, =, \neq\) less than or equal, less than, greater than,greater than or equal, equal to, not equal to Left-to-right
2 not negation (unary NOT) Left-to-right
3 and logical AND Left-to-right
4 or logical OR Left-to-right

Question! Assume that the Boolean variable A is True, B is True and C is False. Using the above precedence rules for the logical operators, evaluate whether the following statements are true or false:

  1. A and B or C
  2. A or B and C
  3. A and C or B
  4. A or C and B
  5. A or C or B
  6. A and B and C
  7. (A or C) and (B or C) or C

Note that in most programming languages, including Python, the equality and inequality operators are written as follows (pay special attention to equality, and notice how it is different from the assignment operator):

Mathematical Operator Python Symbol
\(<\) <
\(\le\) <=
\(>\) >
\(\ge\) >=
\(=\) ==
\(\neq\) !=

3.2.2 Short-circuit operators

In many languages, including Python, the Boolean operators and and or are short-circuit operators. What does this mean? Well, it means that if a Boolean expression can be determined at some point, the rest of the expression is not evaluated. As an example, look back at the truth tables above. You’ll notice that if we’re evaluating A and B, and A is false, then the expression is false regardless of B’s value! Therefore, Python will not even inspect B — it will just say that the expression is false. Similarly, when computing A or B, if A is true, then the expression is true regardless of B’s value! The ability to determine the truth of a Boolean expression without inspecting all terms is what we mean by short-circuit operators.

While this may not seem useful at first, it can actually save a lot of computation. For example, imagine we have a mathematical expression that is extremely long and takes a while to execute (e.g. pow(x, 4.2) * tan(x) + sqrt(x) - ...). Now if we wish to compute A or pow(x, 4.2) * tan(x) + sqrt(x) - ... > 0 and we know that A is true, then we can just completely skip the long equation, since we already know that the expression will be true!

Question! Assume that the Boolean variable A is True, B is True and C is False. Using the precedence rules and the knowledge of short-circuit operators, which terms are not evaluated in the expressions below?

  1. A and C
  2. C and A
  3. C and A and B
  4. C and A or B
  5. B or C and A

3.3 Conditional Statements

Having looked at Boolean expressions and operators, we can now return to our initial discussion of conditional statements. These statements allow us to run certain lines of code, but only when certain conditions are met! For example, if we want a program that outputs a list of students who have passed the course, then we want student names to be printed only if their grade is greater or equal to 50.

Informally, these are known as if-statements, and in pseudocode, they look something like this:

  if logical_expression:
     statement_1
     statement_2

In the above, the logical_expression is a Boolean expression that is evaluated as true or false. If that expression evaluates to true, the lines of code below it are executed. Otherwise, those lines are ignored! The lines of code “inside” the if statement (shown above with indentation) are known as the body of the if statement. The code in the body is executed only if the condition is met; all other lines of code not in the body are executed as normal. Let’s now look at an example of a program that will accept a student’s mark and print out the word “Passed,” but only if they have a sufficiently high mark.

For reference, the code in the above video is the following:

grade = int(input())
if grade >= 50:
   print("Passed")

print("Program terminating...)

Some other examples of if statements are below. Try predict what the output of the code will be.

if 2 < 3:
   print("2 is less than 3")

if 2 != 3:
   print("2 is not equal to 3")

if 2 > 3:
   print("2 is greater than 3")

Question! Write a program that accepts two marks as integers from input. Display whether both marks are above 50, only one is above 50, or neither are.

3.3.1 Else Clauses

In the above example, we used if statements to execute lines of code if a condition was met. Otherwise, those lines were skipped, and the program continued with the next lines. But what if we also want to execute certain lines if the condition is false as well?! In our example of passing, let’s say we want to output “Passed” if the student has passed, but also now output “Failed” if they haven’t. To do that, we need the if-else statement, which says: execute these lines of code if true, but otherwise execute these other lines of code instead. An example of this is below.

grade = int(input())
if grade >= 50:
   print("Passed")
else:
   print("Failed")

print("Program terminating...)

3.3.2 Elif Clauses

We now have the ability to handle two cases: when the statement is true, and when it is false. But what if we want to handle multiple cases? For example, if we’re given a mark, perhaps we want to output a symbol (e.g. A, B, etc). How do we do that?

In Python, this is done with the elif clause (elif is short for else if). This allows us to check multipe expressions to find the first one that is true, and execute the corresponding lines of code. Once that true condition is found, the rest of the statements are skipped. An example of how we would go about that is below.

grade = int(input())
if grade >= 75:
   print("A")
elif grade >= 70:
   print("B")
elif grade >= 60:
   print("C")
elif grade >= 50:
   print("D")
elif grade < 50:
   print("F")

print("Program terminating...)

Note also that we can integrate our else clause into the above if we wanted to. The else acts as a “catch-all” for the case where we found no true expressions, and essentially says: only if nothing above was true, execute these lines of code. As such, the else is optional, and there can only be one else clause at most! In the above example, we might wish to use an else clause to handle any unexpected input, as follows:

grade = int(input())
if grade >= 75 and grade <= 100:
   print("A")
elif grade >= 70:
   print("B")
elif grade >= 60:
   print("C")
elif grade >= 50:
   print("D")
elif grade < 50 and grade >= 0:
   print("F")
else:
   # if none of the above were true, then we execute this line!
   print("Invalid input! Expected value in range [0, 100])

print("Program terminating...)

3.3.3 Nested ifs

If statements (as well as elifs and elses) can be used to execute any number of lines of code. Since we can put any code inside an if statement, one thing we can do is put other if statements inside! We call these nested if statements, and an example of them is below:

num = int(input())
if num % 2 == 0:
   if num % 3 == 0:
      print("Divisible by 2 and 3")
   else:
      print("Divisible by 2; not divisible by 3")
else:
   if num % 3 == 0:
      print ("Divisible by 3; not divisible by 2")
   else:
      print ("Not divisible by 2; not divisible by 3")

Notice in the above that there are multiple indentations. The indentations are used to indicate which lines of code “belong” to which if and else clauses. Try see if you can trace through the above example by hand, and then watch the video below to validate that you were correct.

Question! Rewrite the program above so that instead of using nested if statements, you only make use of if, elif and else clauses. When you’ve done this, try to run your code and input various numbers to verify that it’s correct. Hint: you will need to make use of and and or operators.

Optimisation side quest! As you will learn in BCO, a computer is made up of a central processing unit, and memory to store variables and results. However, there are different levels of memory — main memory is large but slow, and cache memory is fast but small. One problem with conditional statements is that the computer needs to wait to determine whether they are true or false before they know which lines of code to execute next. This means it cannot, for example, preload data into cache memory, since it doesn’t know what data it will need. Or does it?!

Use the internet to learn about branch prediction, where the computer will guess the outcome before it even executes it! The CPU will make a guess and proceed as though that guess was correct. If it turns out it was, then it has achieved a speedup. But if not, then it must throw away those results! Now find out about a computer virus called Spectre and how it takes advantage of this to steal people’s passwords and private data!