Control structures#

Learning goals

After finishing this chapter, you are expected to

  • use if, elif, and else

  • write while loops

  • write for loops

Conditional statements with if#

Now that you are able to write and run scripts in VS Code, it is time to do some more interesting programming. So far, your scripts have been quite predictable. They are a series of statements that are executed from top to bottom, i.e., sequential execution. One nice feature of almost all programming languages is that they allow the use of control structures that direct the execution of your program. One of the most commonly used control structures is the conditional statement if. An if statement looks like this:

if <expr>:
    <statement>

Here, <expr> is a boolean expression, i.e., something that takes on the value True or False. The <statement> is a valid Python statement, something that will be executed if <expr> evaluates to True. The colon : is part of the Python syntax and should always be placed after the boolean expression. Further, note that there is some whitespace in front of the <statement>. This is called indentation and is also part of the Python syntax. Python uses four spaces as indentation. You usually don’t have to care of this yourself: the interpreter and any IDE that knows that you’re programming in Python will help you automatically use that indentation when necessary.

To get a better feeling about the behavior of the if-statement, take a good look at the following code examples. Note that the interpreter only prints yes if the <expr> is True. Otherwise, nothing happens. Also, note that if y gets evaluated to True. This is so because Python evaluates any integer value larger that is not zero as True (we have also seen this in the previous chapter).

x = 0
y = 5

if x < y:           # True
    print('yes')      
yes
if y < x:           # False
    print('yes')    
if x:               # False
    print('yes')    
if y:               # True
    print('yes')    
yes

In the previous chapter, you have used the and, or and not logical operators that evaluate to a boolean. You can also use these in the expression of your if statement, as in the examples below.

if x or y:          # True
    print('yes')    
yes
if x and y:         # False
    print('yes')    
if not x:           # True
    print('yes')
yes

Blocks#

The <statement> that follows an if condition in Python does not necessarily have to be just one line of code. In fact, you can add a block or suite of statements after your if condition. As long as you stay at the same indentation level, these will be jointly executed with the first statement. For example, based on the cases above, we could have the following piece of code:

if y: 
    print('y')
    print('is a non-zero integer')
    print('so I will print')
    print('yes')
y
is a non-zero integer
so I will print
yes

Here, all the print statements form a block of code at the same indentation level. Within a block, you can have additional if statements. For example, instead of writing if x and y: as we did above, you could also write the following to get the exact same behavior. Notice that now we have added two levels of indentation.

if x:
    if y:
        print('yes')

Blocks can be nested to arbitrary depth, and depending on the expressions, some lines will be executed while others won’t.

Exercise 3.1

One of the lines in the following code is not executed. Which line is it?

if 'foo' in ['foo', 'bar', 'baz']:       
    print('Outer condition is true')      

    if 10 > 20:                           
        print('Inner condition 1')        

    print('Between inner conditions')     

    if 10 < 20:                           
        print('Inner condition 2')        

    print('End of outer condition')       
print('After outer condition')            

else and elif#

As you might expect, where there’s an if there can also be an else. This else is an expression which is evaluated as the opposite of the if expression. The statement following this expression is what gets executed if the expression following if is evaluated to be False.

For example, take the following piece of code. In this case, the expression following if is obviously False, so the statement following else will be executed and ‘smaller’ will be printed.

if 10 > 20:
    print('larger')
else:
    print('smaller')
smaller

A third kind of expression is the elif or ‘else if’ expression that can be used in case there are more than two possible outcomes. Following an if expression, you can have any number of elif expressions that you desire, potentially followed by an else statement. Note that if the expression following if is True, none of the other expressions will actually be checked. Similarly, as soon as one of the elif conditions is true, the other conditions will not be checked. The following provides an example of using if, elif and else.

language = 'french'
if language == 'english':
    print('hello')
elif language == 'dutch':
    print('hallo')
elif language == 'french':
    print('bonjour')
else:
    print('unknown language')
bonjour

Exercise 3.2

In each of the following equations, evaluate the given code fragments. Investigate each of the fragments for the various starting values. Use Python to check your answer.

  1. What is the value of m if
    a) n = 7
    b) n = 0
    c) n = -7 ?

        if n > 1:
            m = n + 2
        else:
            m = n - 1
    
  2. What is the value of t if
    a) s = 1
    b) s = 7
    c) s = 57
    d) s = 300

    if s <= 1:
        t = 2*s
    elif s < 10:
        t = 9 - s
    elif s < 100:
        t = s**2
    else:
        t = s
  1. What is the value of h if
    a) t = 50
    b) t = 19
    c) t = -6
    d) t = 0

    if t >= 24:
        h = 3*t + 1
    elif t < 9:
        h = t**2/3 - 2*t
    else:
        h = -t
  1. What is the value of y if
    a) x = -1
    b) x = 5
    c) x = 30
    d) x = 56

    if 0 < x < 7:
        y = 4 * x
    elif 7 < x < 55:
        y = -10 * x
    else:
        y = 333

Exercise 3.3

A bank will offer a customer a loan if they are 21 or over and have an annual income of at least €21000. Write a Python script that contains the customer’s age and income in a dictionary. Depending on the age and income, one of the following lines should be printed (using the print function):

  • ‘We are able to offer you a loan.’

  • ‘Unfortunately at this time we are unable to offer you a loan.’

Verify your code by testing with

  • A 40-year old man with an annual income of €11000

  • A 18-year old woman with an annual income of €25000

  • A 30-year old woman with an annual income of €40000

  • A 15-year old boy with an annual income of €5000

Exercise 3.4

Write a Python script that asks the user for two numbers and prints ‘The first number can be divided by the second number’ or ‘The first number cannot be divided by the second number’, depending on which is the case.

Conditional expression

In addition to the syntax above, Python offers a compact way of writing binary if/else statements. This is called a conditional expression or ternary operator and means that the expression

if 10 > 20:
    print('larger')
else:
    print('smaller')

can also be written in one line of code as

print('larger') if 10 > 20 else print('smaller')

It can in some cases be useful two write expressions like this, for example when you want to compactly assign a value to a variable.

coat = 'raincoat' if raining else 'jacket'

while loops#

Iteration control structures, loops, are used to repeat a block of statements until some condition is met. Python supports two types of loops: the while-loop and the for-loop. A while loop executes some statement as long as a condition is True. We call each time that the statement below while is executed an ‘iteration’. As soon as the condition is False the loop will stop iterating.

a = 1
while a < 10:
    print(a)
    a += 2

The animation below nicely visualizes how a while loop works in this piece of code. As you can see, the while statement is checked at the beginning of each iteration. If that is the case, the block below the while statement is executed. If not, the loop ends.

whileloop

You can use while loops for situations in which you do not know beforehand how many iterations you need. For example, we can write a script that uses a while loop to create the longest possible list of even numbers that together are less than 40.

even_sum = 0
n = 0
even_numbers = []


while even_sum < 40:
    even_sum += n
    even_numbers.append(n)
    n += 2

print(even_sum)
print(even_numbers[:-1])
42
[0, 2, 4, 6, 8, 10]

If you look closely at this example, you can see that even_sum is actually larger than 40. This is so because at the moment that even_sum < 40 became False (and the loop stopped), the last number n=12 had already been added to even_numbers. Hence, we have to use slicing to exclude the last number in the print statement.

We could prevent this by using an if expression and a break statement. Once Python encounters a break, it immediately exits whatever loop it is in. In this case, the code could be changed to the following.

even_sum = 0
n = 0
even_numbers = []

while even_sum < 40:
    if even_sum + n >= 40: # Check if adding the current number n would let even_sum exceed 40
        break              # If that is the case, get out of the loop immediately
    else:                  # If not, continue looping
        even_sum += n
        even_numbers.append(n)
        n += 2 
print(even_sum)        
print(even_numbers)
30
[0, 2, 4, 6, 8, 10]

In addition to break, Python also contains a continue statement. If Python encounters a continue statement, it stays in the loop, but ends the current iteration. In our example, imagine that (for some reason) we do not want to include a number n if it can be divided by 6. Then we can add an elif statement to check this condition and skip the current iteration if the condition is met.

even_sum = 0
n = 0
even_numbers = []

while even_sum < 40:
    if even_sum + n >= 40: # Check if adding the current number n would let odd_sum exceed 40
        break              # If that is the case, get out of the loop immediately
    elif n % 6 == 0:       # If the number n can be divided by 6
        n += 2             # Increment the number
        continue           # Move on to next iteration
    else:                  # If neither condition is met, continue looping
        even_sum += n
        even_numbers.append(n)
        n += 2 
print(even_sum)  
print(even_numbers)
38
[2, 4, 8, 10, 14]

Infinite loops

A while loop will continue running as long as its condition is True. That means that it’s also possible to make an infinite loop that causes your code to get stuck. For example, the following code will run forever if you don’t stop it.

while True:
    print('Running')

If you make such a loop, always make sure that there is also a break statement inside (in combination with an if) so you an actually exit the loop. If you accidentally do end up in an infinite loop, you can in most terminals use Ctrl + c to shut down your script.

Exercise 3.5

Write a script that determines the largest integer \(n\) for which \(1^3 + 2^3 + \cdots + n^3\) is less than 2000.

Exercise 3.6

Write a guessing game where the user has to guess a secret number. After every guess the program tells the user whether their number was too large or too small. At the end of the game, the number of tries needed by the user should be printed. If a user adds the same number twice in a row, it counts as one try.

Start your script with the following code to define the secret number:

import random

secret_number = random.randint(0, 50)

# The rest of your code goes here

for loops#

In contrast to a while-loop, for which the number of iterations is not defined, in a for-loop the number of iterations is fixed upon entering the loop. The standard for-loop has general syntax

for item in <sequence>:
    do something 

Here, <sequence> can be a list, set, tuple, dictionary, or string. For example, we can print all elements in the list [1, 2, 3, 5, 8] as follows

for item in [1, 2, 3, 5, 8]:
    print(item) 
1
2
3
5
8

The for-loop iterates over something which we call an iterable: an object that is able to return its items one-by-one until no more items remain. For example, if we want to print ‘Hello world’ five times, we can make a list with five items (here digits) and iterate over that list.

for item in [1, 2, 3, 4, 5]:
    print('Hello world')
Hello world
Hello world
Hello world
Hello world
Hello world

Note that in the example above, we actually don’t do anything with the items in the list. We have here used integers in ascending order, but basically any item of any type will do. In contrast to other programming languages, Python does not use these integers as indices unless you want it to do so. We do have to give a name to the variable that holds the value coming from the list, but we could also use the ‘throwaway’ variable name _. To illustrate this, the previous result could also have been obtained with strings in a list.

for _ in ['person', 'woman', 'man', 'camera', 'tv']:
    print('Hello world')
Hello world
Hello world
Hello world
Hello world
Hello world

Now let’s say that we want to do something a 1000 times. For example, add all integers between 0 and 1000. You can imagine it would be very cumbersome to write out a list with 1000 integers to iterate over. Luckily, the built-in range(start, stop, step) function in Python returns an iterable that provides numbers from start to stop with an interval of step. Now, to print ‘Hello world’ 5 times, we simply use.

for _ in range(0, 5, 1): 
    print('Hello world')
Hello world
Hello world
Hello world
Hello world
Hello world

The arguments of the range function work very similar to slicing that you have seen for lists and tuples. The function returns integers up to but not including the stop argument. Moreover, step sizes can be negative. Take a look at the following examples.

for digit in range(0, 5, 1):
    print(digit)
print('---')

for digit in range(1, 9, 2):
    print(digit)
print('---')

for digit in range(9, 2, -1):
    print(digit)
0
1
2
3
4
---
1
3
5
7
---
9
8
7
6
5
4
3

As mentioned above, your for loop can iterate over lists, tuples, sets, dictionaries, and strings. For example, we can loop over the letters in a string.

some_string = 'coffee'
for letter in some_string:
    print(letter)
c
o
f
f
e
e

break and continue

Just like in a while loop, you can use break and continue to exit the loop or skip iterations in a for loop.

Exercise 3.7

Write short scripts using loop constructions for the following tasks.

  1. Determine the sum of the first 50 squared numbers with a loop.

  2. Create a script with just one loop that calculates the sum of all entries of a vector \(x\) and also the vector of running sums (as a list). The running sum of a vector \(x\) of \(n\) entries is the vector of \(n\) entries defined as \([x_0, x_0+x_1, x_0+x_1+x_2, \cdots, x_0+x_1+\ldots+x_n]\). Test your code for x = \([1, 9, 1, 0, 4]\).

Nested loops#

Just like we can nest conditional statements in blocks, we can also nest loops. For example, we can generate a multiplication table for numbers 1 to 5 with two nested loops.

for i in range(1, 6):
    row = []
    for j in range(1, 6):
        row.append(i*j)
    print(row)
[1, 2, 3, 4, 5]
[2, 4, 6, 8, 10]
[3, 6, 9, 12, 15]
[4, 8, 12, 16, 20]
[5, 10, 15, 20, 25]

There are no restrictions on kinds of loops that you nest: a for loop can contain a while loop and vice versa. Moreover, there are no restrictions on the number of loops that you nest (or the depth). You can, for example, nest three loops as follows.

for i in range(3):
    j = 0
    while j < 7:
        for k in range(2):
            print(i, j, k)
            j = j + 2
0 0 0
0 2 1
0 4 0
0 6 1
1 0 0
1 2 1
1 4 0
1 6 1
2 0 0
2 2 1
2 4 0
2 6 1

Simultaneously looping over two sequences#

Sometimes you might want to loop over two sequences at the same time. An easy example is pairwise multiplication of two vectors: the aim is to compute a new vector in which each element is the product of the elements in the two input item at the same index. For this, you can use the zip function that you have seen before and that acts like a zipper conjoining two lists. See below for an example

vector_a = [3, 6, 5] # Define an input vector as a list
vector_b = [2, 4, 8] # Define a second input vector
vector_c = []        # Define an output vector 

for elem_a, elem_b in zip(vector_a, vector_b): # Jointly iterate over the two input vectors
    vector_c.append(elem_a * elem_b)           # Append product of elements to output vector
    
print(vector_c)
[6, 24, 40]

Exercise 3.8

Write a script that does the following

  • ask the user for a number \(n\)

  • give the user the choice between computing the sum and computing the product of \(1,\ldots,n\)

  • compute the desired result

  • print the result for the user

Exercise 3.9

Write a program that uses loops to compute Fibonacci numbers (as in Exercise 1.12). The scripts asks the user how many numbers should be computed. This can be any number, so your code should be flexible.