Steering the control flow of a program¶

Up to this point, we have already learned how to utilise Python and Jupyter Notebooks to solve a variety of programming scenarios with the help of the essential data types and structures. We also get to know the usage of external packages and the usage of user-defined functions and their vast benefits. Nevertheless, the shown programming approaches have been pretty straightforward so far in a manner of speaking that we only implemented so-called sequentially control flows. This programmatic concept describes a relatively static way of implementation since all our implemented lines of codes (statements) are executed exactly in the order they are written without any possibility of omission. Hence our programming approaches lack the opportunity for flexibility, like a repeated or conditioned execution of certain code blocks.

Therefore, within the scope of this chapter, we want to get to know a variety of standard control flow structures that will allow you to modify and steer the programming flow in your favour. The following control flows will be introduced:

  • if, elif and else
  • while
  • for

Conditioned code execution¶

The first way of steering the code execution is to create branches within the execution flow of your code using conditions. The condition itself must contain a logical statement which is evaluated. Depending on the outcome of the logical expression (either True or False), one or the other branch is executed. We use the' if' statement to implement this type of control flow. The general syntax is:

In [ ]:
if (logical_expression):
    # do a thing
    statement_1
    statement_2
else:
    # do something else
    statement_3
    statement_4

For example, we could implement a UDF that proves based on your age, if you are a teenager or an adult and returns the results of that evaluation:

In [1]:
def age_test(age):
    if (age < 18):
        return("You are a teenager!")
    else:
        return("You are an adult!")

Let us test our nearly implemented, conditioned control flowed UDF by a few different values:

In [2]:
age_test(14)
Out[2]:
'You are a teenager!'
In [3]:
age_test(30)
Out[3]:
'You are an adult!'

Isn't it great?! Based on our logical expression, the age check age < 18, and a different output is produced at the given age! Great. Since our example only covers a dichotomic categorisation based on age, we want to implement a broader range of outputs based on the given age. Therefore we make use of the elif statement and adapt our age_test() function:

In [4]:
def age_test(age):
    if (age <= 0):
        return("You are not born yet!")
    elif(age <= 10):
        return("You are a kid!")
    elif(age < 18):
        return("You are a teenager!")
    elif(age < 65):
        return("You are an adult!")
    else:
        return("You are a retiree!")

Let's try our new version of the function with a few ages:

In [5]:
age_test(5)
Out[5]:
'You are a kid!'
In [6]:
age_test(-5)
Out[6]:
'You are not born yet!'
In [7]:
age_test(70)
Out[7]:
'You are a retiree!'

Using loops for repetition of statements¶

Another way to steer the control and execution flow is by repeatedly executing a specific code block on purpose with changing values. This programmatic concept is related to the term loops. It is one of the most powerful since it allows you to save many unnecessary LoCs due to little differences. The code statements within the loop are repeated as long as a given condition remains True or False based on the chosen implementation or as long as not all elements of a given list are selected once. The first case is related to while loops, while the second holds for for loops. For loops in Python are helpful when you want to cycle over all of the items in a collection (such as all of the elements of an array), and while loops are useful when you want to cycle for an indefinite amount of time until some condition is met.

for loops¶

At first, we want to look at for loops since the structure is most likely easier to follow as a beginner.

For a first example, let us use a for loop to print each number between 1 and 10 sequentially:

In [8]:
for i in range(10):
    print(i)
0
1
2
3
4
5
6
7
8
9

Oh, that was relatively easy, but the result is unexpected! The reason is that we do not use the range command appropriately. Since the counting in Python starts with 0, we must adapt the statement accordingly because we want the first ten numbers without the 0. Hence:

In [9]:
for i in range(1, 11):
    print(i)
1
2
3
4
5
6
7
8
9
10

Note: the range command generates a list of values according to the parametrisation. If only one parameter is given, the list will start with $0$ and ends with $n - 1$. If two parameters are given, the list starts with $a$ and ends with $n - 1$.

Awesome! As a next, more advanced task we want to calculate the sum of the first ten numbers with the help of a for loop:

In [10]:
result = 0
for i in range(11):
    result = result + i
print(result)
55

Note on indentation: Notice the indentation once we enter the for loop. Every idented statement after the for loop declaration is part of the for loop. This rule holds true for while loops, if statements, functions, etc. Required identation is one of the reasons Python is such a beautiful language to read.

Nice! We can also simplify the loop by the use of the += operator:

In [11]:
result = 0
for i in range(11):
    result += i
print(result)
55

Exercise: Externalise the above code within a UDF named sum_of_numbers(). The UDF shall take one parameter. Adapt the code so that this parameter steers up to which natural number the sum will be calculated.

In [ ]:
### your solution
In [12]:
Show code
def sum_of_numbers(a):
    result = 0
    for i in range(a + 1):
        result += i
    return(result)

Test your function:
The sum of the first $100$ natural numbers should be $5050$, while the sum of the first $500$ natural numbers should be $125250$.

In [13]:
sum_of_numbers(100)
Out[13]:
5050
In [14]:
sum_of_numbers(500)
Out[14]:
125250

We may as well loop through any iterable object, such as a list of strings. Let us create a list of different names for those purposes:

In [15]:
names = ["John", "Ringo", "Paul", "George"]

Now let's print each name in the list one by one:

In [16]:
for name in names:
    print(name)
John
Ringo
Paul
George

Note: By using in, each value within the list names is assigned sequentially and once to the variable name once! That makes the for loop quite handy and clearly implemented.

Exercise: Combine the items from the two given lists names and surnames and print them as one string to the console each!

Hint: You may want to investigate the use of the enumerate() and the zip() functions on your own with the help of the internet to find a handy and short solution for this task!

In [17]:
names = ["John", "Ringo", "Paul", "George"]
surnames = ["Lennon", "Star", "McCartney", "Harrison"]
In [ ]:
### your solution
In [18]:
Show code
for i, name in enumerate(names):
    print(name, surnames[i])
    print("-----")
John Lennon
-----
Ringo Star
-----
Paul McCartney
-----
George Harrison
-----
In [19]:
for name_comb in zip(names, surnames):
    print(name_comb[0], name_comb[1])
    print("-----")
John Lennon
-----
Ringo Star
-----
Paul McCartney
-----
George Harrison
-----

while loops¶

A while loop repeats a task until a particular condition is met. Therefore, we do not need to know how often the loop will be executed. Be sure there is a way to exit out of a while loop. Otherwise, your program will run forever.

Let's implement the same task with which we introduced the for loop, but this time by using a while loop. That means using a while loop that prints the first ten natural numbers sequentially:

In [20]:
i = 1
while(i <= 10):
    print(i)
    i += 1
1
2
3
4
5
6
7
8
9
10

Now, we want to adapt the above loop so that only every second number starting with $2$ is printed:

In [21]:
i = 2
while(i <= 10):
    print(i)
    i += 2
2
4
6
8
10

There is also the possibility of using an if statement within the loop to achieve the same result. We want to have a look at that solution by printing all even numbers from $0$ to $25$:

In [22]:
number = 0
while (number <= 25):
    if ((number % 2) == 0):
        print(number)
    number += 1
0
2
4
6
8
10
12
14
16
18
20
22
24

Exercise: Write a parameterised UDF called fibo_numbers(), that calculates the fibonacci numbers according to the given parameter! Use a while loop for this task.

In [ ]:
### your solution
In [23]:
Show code
def fibo_numbers(end):
    a = 0
    fibo_list = []
    
    while (a <= (end - 1)):
        if (a < 2):
            fibo_list.append(1)
        else:
            fibo_list.append(fibo_list[a-2] + fibo_list[a-1])
        a += 1
        
    return(fibo_list)

Test your function:
The first 10 fibonacci numbers are:

In [24]:
fibo_numbers(10)
Out[24]:
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

Synthesis - Guess a number¶

Finally, we want to conclude this section by applying our knowledge of UDFs and control statements to build a small program, called guess_a_number. The program is all about guessing a random integer within a given range of guesses. Copy the following two cells to your Jupyter notebook and start the game with the function call guess_a_number(<max_value>, <trials>).

In [25]:
def ask_for_guess(max_value, trial):
    print("Guess a natural number between 1 and ", max_value, "!")
    text = print("Trial ", trial, " - Your guess:")
    guess = input(text)
    
    try:
        guess = int(guess)
        succ = True
    except ValueError:
        succ = False
    
    if (succ == False):
        print("Please provide a natural number!")
        guess = ask_for_guess(max_value, trial)
    
    return guess
In [26]:
import random

def guess_a_number(max_value = 100, number_of_trials = 10):
    """
    Function do initialize the game.
    You can adapt the number of maximum trials as well as the value range
    """ 
    secret_number = random.choice(range(1, max_value))

    trial = 1
    success = False
    
    while (trial <= number_of_trials):
        guess = ask_for_guess(max_value, trial)
        
        if (guess == secret_number):
            print("Congratulation! You have sucessfully guess the right number in ", trial, " trials.")
            success = True
            break
        elif (guess < secret_number):
            print("The number is bigger!")
        else:
            print("The number is smaller!")
        
        print(number_of_trials - trial, " trials left.")
        trial += 1
    
    if (success == False):
        print("Sorry, game over you exceeded your maximum trial count!")
In [ ]:
guess_a_number(10, 3)

Citation

The E-Learning project SOGA-Py was developed at the Department of Earth Sciences by Annette Rudolph, Joachim Krois and Kai Hartmann. You can reach us via mail by soga[at]zedat.fu-berlin.de.

Creative Commons License
You may use this project freely under the Creative Commons Attribution-ShareAlike 4.0 International License.

Please cite as follow: Rudolph, A., Krois, J., Hartmann, K. (2023): Statistics and Geodata Analysis using Python (SOGA-Py). Department of Earth Sciences, Freie Universitaet Berlin.