22. Looping#

Author: Tue Nguyen

22.1. Outline#

  • Overview

  • for loops

  • while loops

  • Control loops with next and continue

  • Infinite loops

  • Comprehensions

22.2. Overview#

  • We often write programs to automate repetitive tasks

  • The benefits are

    • We write a small chunk of code but can repeat it as many times as we want

    • If we want to change something to the code logic, we only have to make modifications in one place

    • This reduces the workload and makes it less prone to errors

  • This repetition is called looping

  • In Python, we can do looping using for or while

22.3. for loops#

22.3.1. Basic for loops#

  • You are already familiar with the basic use of the for loop

  • In this section, we will learn about it in more detail through more interesting examples

  • The template for the for loop is as follows

for item in iterable:
    # Do something

a) Ex 1: say hello

Say hello to a list of people

# Init a list of people
people = ["John", "Bob", "Mary"]

# Write code to say hello to as many people as we want
for p in people:
    print(f"Hello {p}")
Hello John
Hello Bob
Hello Mary

b) Ex 2: looping with enumerate

  • When iterating through a sequence, you might want to access both the value and the index of an element

  • You can do this with enumerate()

  • In each iteration, enumerate() returns a tuple of 2 entries

    • The first one is the index (counting from 0)

    • The second one is the current element of the sequence

# Init a list of players with higher-ranked ones first
players = ["Bill", "Peter", "Tony"]

# Print players with ranking
for i, p in enumerate(players):
    print(f"{p} is ranked {i + 1}")
Bill is ranked 1
Peter is ranked 2
Tony is ranked 3

c) Ex 3: compute a cumulative sum

Compute the sum of elements from a list of numbers

# Init a list of numbers
numbers = [1, 2, -2.5, 30, 4, 7]

# Compute the sum of elements in numbers
total = 0
for n in numbers:
    total += n

# Print the result
total
41.5
# We can double check out result using sum() function
sum(numbers)
41.5

d) Ex 4: see what happens in each iteration

  • In Ex 3, we only see the final result

  • However, we can write a program to see what happens under the hood by moving the print statement inside the loop

# Init
numbers = [1, 2, -2.5, 30, 4, 7]
total = 0

# Loop and print the state of total at each iteration
for n in numbers:
    total += n;
    print(total)
1
3
0.5
30.5
34.5
41.5

e) Ex 5: compute factorial

Compute the factorial for a positive integer

  • Recall that the factorial of a positive number \(n\) is \(n! = 1\times 2\times \dots \times n\)

  • If \(n = 0\) then \(n! = 1\)

  • For simplicity, in this example, we write code for n > 0 only

# Init a number n
n = 6

# Init a variable to for the factorial with value 1
fact = 1

# Loop through a range of ints from 1 to n
# In each iteration, MULTIPLY the current element with fact
for i in range(1, n + 1):
    fact *= i
    
# Check the result
fact
720

f) Ex 6: count the number of characters of each type

Given a sentence in English, count the number of consonants, vowels, and non-cased (non-letter) characters

# Init a string
s = "Hi. How are you?"
# Snippet 1: use nested if
# Init three counter variables
n_consonants = 0
n_vowels = 0
n_non_letters = 0

# Iterate through the lowercase version of s
# and update the counters accordingly
for c in s.lower():
    if c.isalpha():
        if c in "ueoai":
            n_vowels += 1
        else:
            n_consonants += 1
    else:
        n_non_letters += 1
        
# Print results
print(f"{n_vowels} vowels")
print(f"{n_consonants} consonants")
print(f"{n_non_letters} non-letter characters")
6 vowels
5 consonants
5 non-letter characters
# Snippet 2: use if elif to avoid nested if
# Init three counter variables
n_consonants = 0
n_vowels = 0
n_non_letters = 0

# Iterate through the lowercase version of s
# and update the counters accordingly
for c in s.lower():
    if not c.isalpha():
        n_non_letters += 1
    elif c in "ueoai":
        n_vowels += 1
    else:
        n_consonants += 1
        
# Print results
print(f"{n_vowels} vowels")
print(f"{n_consonants} consonants")
print(f"{n_non_letters} non-letter characters")
6 vowels
5 consonants
5 non-letter characters

g) Ex 7: extract numbers of each types

From a list of numbers, extract positive, negative, and zero elements into three lists

# Init a list of numbers
x = [1, 2, -1.5, 0, 7, 9.5, -10, 0, 3.4, -5.5]

# Init 3 variables to hold extracted elements
pos = []
neg = []
zeros = []

# Extract elements
for e in x:
    if e > 0:
        pos.append(e)
    elif e < 0:
        neg.append(e)
    else:
        zeros.append(e)
        
# Print the result
print(f"Positive numbers: {pos}")
print(f"Negative numbers: {neg}")
print(f"Zeros numbers: {zeros}")
Positive numbers: [1, 2, 7, 9.5, 3.4]
Negative numbers: [-1.5, -10, -5.5]
Zeros numbers: [0, 0]

22.3.2. Nested for loops#

  • We can write a for loop inside another for loop

  • This is called a nested for loop

  • However, be careful that computation can explode very quickly if your program involves nested for loops

  • Ex: if the outer loop has \(1,000\) iterations and the inner loop has \(2,000\) iterations, then your program has to perform \(2,000,000\) iterations in total

a) Ex 1: compute factorial for each integer in a list

Loop through a list of positive integers, and at each iteration, print the factorial of that integer

# Init a list of positive integers
numbers = [1, 3, 4, 6, 7]
for n in numbers:
    fact = 1
    for i in range(1, n + 1):
        fact *= i
    print(f"{n}! = {fact}")
1! = 1
3! = 6
4! = 24
6! = 720
7! = 5040

b) Ex 2: print a triangle

Print out the following triangle

0 1 2 3 4 5 6 7 8 9 
1 2 3 4 5 6 7 8 9 
2 3 4 5 6 7 8 9 
3 4 5 6 7 8 9 
4 5 6 7 8 9 
5 6 7 8 9 
6 7 8 9 
7 8 9 
8 9 
9 
for i in range(10):
    for j in range(i, 10):
        print(j, end=" ")
    print("")
0 1 2 3 4 5 6 7 8 9 
1 2 3 4 5 6 7 8 9 
2 3 4 5 6 7 8 9 
3 4 5 6 7 8 9 
4 5 6 7 8 9 
5 6 7 8 9 
6 7 8 9 
7 8 9 
8 9 
9 

22.4. while loops#

22.4.1. Basic while loops#

  • Another way to repeat a block of code in Python is to use a while loop

  • The template is as follows

while condition:
    # Do something
  • Here is how a while loop works

    • First, Python evaluates condition

    • If False, Python skip the code inside while and proceed to the next statement after while

    • If True, Python executes the code inside while, and after that Python goes back to check condition

    • As long as condition is still True, Python continues repeating the code inside while

a) Ex 1: compute the sum of numbers in a list

  • In an earlier example, we saw how to use a for loop to compute the sum of a list of numbers

  • We can also use a while loop to achieve the same thing

# Init a list
x = [1, 2, 10, -5]
x
[1, 2, 10, -5]
# Compute the sum
i = 0
total = 0

while i < len(x):
    # Add x[i] to the total
    total += x[i]
    
    # Important: increase i 
    # to move to the next element
    i += 1
    
# Check the result
print(total)
8

What happened?

  • First, we initiate 2 variables

    • i: to track the iterations

    • total: to accumlate the sum of elements of x

  • In the first iteration

    • At the beginning, i = 0 and total = 0

    • Condition i < len(x) is true because len(x) is 4

    • Thus, Python executes the code inside while

    • The statement total += x[i] add x[0] to total, and now total is 1

    • The statement i += 1 increase by 1 and now i is 1

    • There’s no more statements so Python moves to the next iteration

  • In the second iteration

    • At the beginning, i = 1 and total = 1

    • Python checks the condition i < len(x) and sees that it is still true

    • Thus, Python executes the code inside while and at the end total is updated to 3 and i is updated to 2

  • This process continues until the 4th iteration is done

  • After the 4th iteration, i = 4 and total = 8

  • Python checks the condition is < len(x) as usual and it gets a False (because 4 < 4 is false)

  • Since the condition is false, the loop terminates

  • Important: people often forget to increase the counter (i += 1) and this leads to an infinite loop (because the condition is always true)

b) Ex 2: redo Ex 1 with .pop()

# Init a list
x = [1, 2, 10, -5]

# Compute the sum
total = 0
while x:
    total += x.pop()
    
# Check the result
total
8

What happened?

  • First, we initiate a list with 4 elements

  • In the first iteration of the while loop

    • Python converts x to bool and gets a True (because the list is non-empty)

    • Since the condition is true, Python executes the statement inside while

    • The last element of x (which is -5) is popped out and added to total

    • x now has 3 elements

  • In the second iteration

    • Python repeats checking the condition and still gets a True (because x is still non-empty)

    • And the last element of x (which is 10 now) is added to total

    • x now has 2 elements

  • The above process is repeated until x becomes an empty list

    • When Python checks the condition, this empty list is converted to False

    • The loop ends

22.4.2. Nested while loops#

  • Similar to for, we can also nest another while or for loop inside a while loop

  • Just be aware that the computation can explode with nested loops

22.5. Control loops with break and continue#

  • The break keyword terminates the entire loop and moves the execution to the statement after the loop

  • The continue keyword just skips the current iteration and moves the execution to the next iteration (if any)

22.5.1. break#

Ex: print elements of a list of and stop until encountering the first negative number

# Init a list
x = [1, 2, 0, -3, 7, 4, -5, 8]
x
[1, 2, 0, -3, 7, 4, -5, 8]
# Case 1: also print out the first negative numbers
for e in x:
    print(e)
    if e < 0:
        break
1
2
0
-3

Explanation

  • In the above code, Python iterates through each element of x from left to right

  • In each iteration, Python prints out the element and then checks if the element is negative

  • We can see that in the 4th iteration, this checking condition is true (because -3 < 0)

  • Thus, the loop ends after 4 iterations

# Case 2: do not print out the first negative numbers
# All we need to do is to do the checking first
# If we encounter the first negative number, the loop ends immediately
# and this negative number will never be printed out
for e in x:
    if e < 0:
        break
    print(e)
1
2
0

22.5.2. continue#

Ex: print out positive numbers only

# Init a list
x = [1, 2, 0, -3, 7, 4, -5, 8]
x
[1, 2, 0, -3, 7, 4, -5, 8]
# Print out only positive numbers
for e in x:
    if e <= 0:
        continue
    print(e)
1
2
7
4
8

Explanation

  • In each iteration, Python checks if the current element e is non-negative or not

  • If true (e is non-negative), then Python will execute the continue statement which ignores everything after the if (in this case, the print statement) and move to the next iteration

  • If false (e is indeed positive), then Python will execute sequentially and print out the element

  • Thus, unlike break, continue just skips some code but does not terminate the loop

# Of course, we can solve the above example without using continue
for e in x:
    if e > 0:
        print(e)
1
2
7
4
8

22.6. Infinite loops#

  • Sometimes we are not sure how many iterations will be needed to accomplish a task

  • Thus, we use a while loop with True as the condition

  • This loop will repeat potentially forever

  • To avoid that, we put an if statement inside the loop to break it when some condition is met

Ex: guess a number

  • Suppose you have to randomly choose an integer from 0 to 5

  • My secrete number is 3

  • If the number you choose coincide with my secrete number, the program ends

  • Otherwise, you repeat the choosing again until your number and mine are the same

  • As you might see, this loop potentially repeats forever

# First we need to import random module
# We might also need to set a seed for reproducibility
# (you can safely ignore this for now)
import random
random.seed(1)
# Init 
secrete_num = 3

# Guess
while True:
    guess = random.randint(0, 5)
    print(f"Your guess: {guess}")
    
    if guess == secrete_num:
        print("You guessed correctly")
        break
Your guess: 1
Your guess: 4
Your guess: 0
Your guess: 2
Your guess: 0
Your guess: 3
You guessed correctly

22.7. Comprehensions#

  • Comprehension is a quick way to create an iterable without using a loop

  • Let’s consider the following example: “generate a list of squares of positive integers less than 10”

# Method 1: use a for loop
squares = []
for i in range(9):
    squares.append(i*i)
    
squares
[0, 1, 4, 9, 16, 25, 36, 49, 64]
# Method 2: use list comp
squares = [i*i for i in range(9)]
squares
[0, 1, 4, 9, 16, 25, 36, 49, 64]

22.7.1. List comprehensions#

  • Create a list without using a loop

  • Syntax: [expr for item in iterable if condition]

  • The if part is optional

a) Ex 1: extract grade >= 5 from a list of grades

# Init a list of grades
grades = [1, 3, 2.5, 7, 8, 5, 6.5, 0, 10, 9.5, 2]

# Extract passing grade
passing_grades = [g for g in grades if g >= 5]

# Check the result
passing_grades
[7, 8, 5, 6.5, 10, 9.5]

b) Ex 2: extract customers with names starting with "A"

# Init a list of customers
customers = ["Anna", "Bob", "Jack", "Alexander", "tom", "andrew"]

# Extract customers whose names start with "A" (case-insensitive)
customers_a = [c for c in customers if c.lower().startswith("a")]

# Check the result
customers_a
['Anna', 'Alexander', 'andrew']

c) Ex 3: create a list of absolute paths from a list of file names

# Init
file_names = ["data.csv", "report.docx", "slides.ppt", "analysis.ipynb"]
dir_path = "D:/projects/proj_1/"

# Create absolute paths
abs_paths = [dir_path + f for f in file_names]

# Check the result
abs_paths
['D:/projects/proj_1/data.csv',
 'D:/projects/proj_1/report.docx',
 'D:/projects/proj_1/slides.ppt',
 'D:/projects/proj_1/analysis.ipynb']

22.7.2. Tuple comprehensions#

  • Similar to list comp, however, the output is a generator instead of a tuple (for optimization purposes)

  • Syntax: (expr for item in iterable if condition)

  • The if part is also optional

  • To see the actual elements of the result, we can convert it to a tuple using tuple()

  • No need for examples

22.7.3. Dict comprehensions#

  • Dic comp is a quick way to create a dict without using a loop

  • Syntax: (expr_key: expr_value for item in iterable if condition)

  • The if part is also optional

a) Ex 1: create a dict of lowercase: uppercase

  • Suppose we have a sentence with words separated by spaces

  • Create a dict with words in lowercase as keys and words in uppercase as values

# Init a sentence
s = "I want to learn Python"

# Create the dict
d = {w.lower(): w.upper() for w in s.split()}

# Check the result
d
{'i': 'I', 'want': 'WANT', 'to': 'TO', 'learn': 'LEARN', 'python': 'PYTHON'}

b) Ex 2: extract high-income individuals

  • Suppose we have a dict of people with IDs as keys and incomes as values

  • Extract those with income >= 100,000

# Init a dict
people = {"ID01": 50e3, "ID02": 72e3, "ID03": 105e3, "ID04": 35e3, "ID05": 125e3}

# Extract high-income individuals
high_income = {k: v for k, v in people.items() if v >= 100e3}

# Check the result
high_income
{'ID03': 105000.0, 'ID05': 125000.0}

c) Ex 3: create a dict of letters with their counts

  • Suppose we have a string

  • Create a dict with each letter as a key and its number of occurrences as values

# Init a string
s = "Hello"

# Create the dict
d = {k: s.count(k) for k in set(s)}

# Check the result
d
{'e': 1, 'H': 1, 'l': 2, 'o': 1}

d) Ex 4: create a dict by zipping lists

  • Suppose we have a list of drinks and another list of prices for the drinks

  • Create a dict with drinks as keys and prices as values

# Init drinks and prices
drinks = ["Cappuccino", "Americano", "Espresso", "Macchiato"]
prices = [1.5, 1.2, 1.6, 2.]

# Create a dict with drinks as keys and prices as values
d = {d: p for d, p in zip(drinks, prices)}

# Check the result
d
{'Cappuccino': 1.5, 'Americano': 1.2, 'Espresso': 1.6, 'Macchiato': 2.0}

22.8. Summary#

Overview

  • Looping is repeating a block of code multiple times for the purpose of automating repetitive tasks

  • Benefits

    • Write once, run as many times as we want

    • If we want to modify something, we have to make changes in one place

    • This reduces manual work and makes it less prone to errors

  • In Python, we can do looping using for or while

for loops

  • Syntax

    for item in iterable:
        # Do something
    
  • Each element of iterable will be visited once from left to right

  • Each visit is called an iteration

  • We can access both the elements and the corresponding indices using enumerate()

  • A for loop can be nested in another for loop (be aware of computation explosion)

while loops

  • Syntax

    while condition:
        # Do something
    
  • As long as condition is true, the code block inside while is still executed (each execution is an iteration)

  • When condition is false, the loop ends

  • The while loop is often used when we are not sure how many iterations are needed (as in the case of an infinite loop)

break and continue

  • The break keyword terminates the entire loop and moves the execution to the statement after the loop

  • The continue keyword just skips the current iteration and moves the execution to the next iteration (if any)

  • For break, it is possible that the loop does not iterate over all elements of the iterable. However, for continue, every element of the iterable is visited

Infinite loops

  • We use an infinite loop (while True:) when we are not sure how many iterations will be needed to accomplish a task

  • Remember to put an if statement inside the loop to check for the exit condition

Comprehensions

  • Comprehension is a quick way to create an iterable without using a loops

  • We have

    • List comprehensions

    • Tuple comprehensions

    • Dict comprehensions

  • Note that comprehensions are not meant to replace loops