Loops (while and for)#
Education objectives
blocks
whileandforkeywords
in,breakandcontinuebuilt-in functions
rangeandenumerate
Loops with the keyword while#
i = 0
while i < 4:
i += 1
print("i =", i)
i = 4
i = 0
while i < 4:
i += 1
print("i =", i)
i = 1
i = 2
i = 3
i = 4
Exercise 9
Edit a script with Spyder that calculates the average of a set of numbers. For example
numbers = [67, 12, 2, 9, 23, 5]using the functions
sumandlenmanually (without
sum), using the keywordwhilecheck that the 2 methods give the same results with
assert avg0 == avg1Run the script
in Spyder,
in a IPython session opened from another terminal,
with the command
python.
Solution to Exercise 9
numbers = [67, 12, 2, 9, 23, 5]
avg0 = sum(numbers) / len(numbers)
tmp = 0
i = 0
while i < len(numbers):
tmp += numbers[i]
i = i + 1
avg1 = tmp / len(numbers)
assert avg0 == avg1
Simulating do while stop_condition construction#
while True:
if stop_condition:
break
# content of the do-while loop
Loops with the keyword for#
values = range(5)
for i in values:
print("i =", i)
i = 0
i = 1
i = 2
i = 3
i = 4
The build-in function range() is very useful for loops. It creates a range object,
which is an iterable, immutable sequence of numbers.
# syntax is range(start, stop, step)
range(0, 4, 1)
range(0, 4)
(default start is 0, default step is 1)
range(4)
range(0, 4)
list(range(1, 8, 2))
[1, 3, 5, 7]
range() is memory efficient because it does not store all the numbers in memory.
range(1_000_000_000_000_000)
range(0, 1000000000000000)
Warning
Do not try list(range(1_000_000_000_000_000)) at home! Can you estimate the order of
magnitude of memory it would use?
It is common to use range() directly in for loops:
for idx in range(4):
print(idx, end=", ")
0, 1, 2, 3,
for loops are of course not limited to integers:
groceries = ["Carrots", "Cabbage", "Milk", "Onions", "Pepper"]
print("Groceries list")
for grocery in groceries:
print("-", grocery)
Groceries list
- Carrots
- Cabbage
- Milk
- Onions
- Pepper
The built-in function enumerate is very useful to access indices
print("My top 5 groceries:")
for index, grocery in enumerate(groceries):
print(f"{index}. {grocery}")
My top 5 groceries:
0. Carrots
1. Cabbage
2. Milk
3. Onions
4. Pepper
Loops: keywords continue and break#
continue: passes the block in the loop and continues the loop.
for x in range(1, 8):
if x == 5:
continue
print(x, end=", ")
1, 2, 3, 4, 6, 7,
break: stop the loop.
for x in range(1, 8):
if x == 5:
break
print(x, end=", ")
1, 2, 3, 4,
Exercise 10
Extend your script with another method (using a
forloop) to compute the average.In IPython, try to understand how the function
enumerateworks. Use it in your script.
Solution to Exercise 10
l = [67, 12, 2, 9, 23, 5]
# simple implementation with sum and len
avg0 = sum(l) / len(l)
# now with for and without sum
avg2 = 0
for e in l:
avg2 += e
avg2 /= len(l)
# now with for and enumerate, but without sum and len
avg3 = 0
for i, e in enumerate(l):
avg3 += e
avg3 /= i + 1
# and now let's check:
assert avg2 == avg0
Exercise 11
We build a list:
from random import randint, shuffle
n = 20
i_removed = randint(0, n - 1)
print("integer remove from the list:", i_removed)
numbers = list(range(n))
numbers.remove(i_removed)
shuffle(numbers)
print(f"shuffled list:\n {numbers}")
integer remove from the list: 7
shuffled list:
[15, 4, 5, 18, 6, 8, 3, 1, 0, 12, 16, 11, 14, 9, 13, 17, 19, 10, 2]
One element has been removed:
Find this element (given that you can change the ordering of
numbers).Find this element (given that you cannot change the ordering of
numbers).
Solution to Exercise 11
# we can change ordering, let's sort
print(numbers)
l_sorted = sorted(numbers)
print(l_sorted)
missing = None
for idx, elem in enumerate(l_sorted):
if elem != idx:
missing = idx
break
if missing is None:
missing = len(numbers)
print(f"{missing = }")
assert missing == i_removed
[15, 4, 5, 18, 6, 8, 3, 1, 0, 12, 16, 11, 14, 9, 13, 17, 19, 10, 2]
[0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
missing = 7
# we cannot sort -> higher complexity
for elem in range(len(numbers) + 1):
if elem not in numbers:
break
missing = elem
print(f"{missing = }")
assert missing == i_removed
missing = 7
# another solution
actual_sum = sum(numbers)
len_numbers = len(numbers)
original_sum = (len_numbers + 1) * (len_numbers) // 2
missing = original_sum - actual_sum
print(f"{missing = }")
assert missing == i_removed
missing = 7
list: list comprehension#
They are iterable so they are often used to make loops. We have already seen how to use
the keyword for. For example to build a new list (side note: x**2 computes x^2):
l0 = [1, 4, 10]
l1 = []
for number in l0:
l1.append(number**2)
print(l1)
[1, 16, 100]
There is a more readable (and slightly more efficient) method to do such things, the list comprehension:
l1 = [number**2 for number in l0]
print(l1)
[1, 16, 100]
# list comprehension with a condition
[s for s in ["a", "bbb", "e"] if len(s) == 1]
['a', 'e']
# lists comprehensions can be cascaded
[(x, y) for x in [1, 2] for y in ["a", "b"]]
[(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b')]
Exercise 12 (advanced)
Write a function
extract_patterns(text, n=3)extracting the list of patterns of sizen=3from a long string (e.g. iftext = "basically", patterns would be the list['bas', 'asi', 'sic', ..., 'lly']). Use list comprehension, range, slicing. Use a sliding window.You can apply your function to a long “ipsum lorem” string (ask to your favorite web search engine).
Solution to Exercise 12 (advanced)
text = "basically"
def extract_patterns(text, n=3):
pat = [text[i : i + n] for i in range(len(text) - n + 1)]
return pat
print("patterns=", extract_patterns(text))
print("patterns=", extract_patterns(text, n=5))
patterns= ['bas', 'asi', 'sic', 'ica', 'cal', 'all', 'lly']
patterns= ['basic', 'asica', 'sical', 'icall', 'cally']