More about loops

Authors

Louis Moresi

Andrew Valentine

Summary

In this section we go deeper into the concept of loops with the concept of a for loop and discuss the concept of an iterable object (a python object that returns a sequence of values one after the other when asked).

We have already encountered one way of repeating calculations - the while loop. Python also provides a second kind of loop, the for loop. This allows us to do a calculation for each of the entries in a list, tuple or dictionary (or, more generally, any object that can yield a well-defined return value each time it is called).

The For loop

Typically, a for loop takes the form:

for <variable> in <collection>:
    [calculations using <variable>]

As with the while loop, everything within the indented block is executed multiple times, with the <variable> getting set equal to a different element of <collection> on each pass. <collection> may be any ‘iterable’, such as a list. For example, the following code would calculate the sum of all the entries in a:

a = [1, 5, 9]
s = 0
for x in a:
    s += x
    print(x, s)
print("Total is:", s)

It is possible to use continue [go to the next iteration immediately] and break [terminate the loop immediately] statements within a for loop, just as in a while loop.

Often, we simply want to perform a calculation \(n\) times, counting off as we go. To allow this, Python provides the range function, which is a special kind of iterable. It can be used as follows:

n = 20
for i in range(n):
    print(i)

By default, the counting starts from 0, and continues up to \(n-1\) (so that there are a total of \(n\) passes through the loop). If you want to start from a different number, you can provide this:

istart = 3
istop = 15
for i in range(istart, istop):
    print(i)

Notice that the counting starts with istart, and finishes with istop-1. We can also count in increments of istep:

istart = 3
istop = 15
istep = 4

for i in range(istart, istop, istep):
    print(i)

This will print istart, istart + istep, istart + 2*istep etc, but not any value equal to or greater than istep.

Rewrite our previous while loop test-case as a for loop. Note that the full syntax of the range function is range(start, end, step) which allows you to skip by 5 as we previously did. You may need to read the help and try the examples above.

x = 1
while x < 100:
    x+=5
    if x == 71: 
        break
    if x%2 == 0: 
        continue # Skip even values of x
    print("In loop: x=", x)

print("After loop: x=", x)

A loop to calculate \(\pi\)

The value of \(\pi\) can be found via an infinite sum \(\pi = 2 \sum_{k=0}^\infty 2^k (k!)^2 / (2k+1)!\), where \(n!\) denotes the factorial of \(n\), i.e., \(n! = n(n-1)(n-2)\ldots 1\). This can be computed by initialising a variable to contain the value 1, and then looping through the integers up to n multiplying the variable by each.

By summing the first \(N\) terms in the series, compute an approximation to \(\pi\) and explore how the accuracy of the approximation changes with \(N\). A good starting point is \(N = 20\).

# A series that converges to the value of $\pi$

enumerations

Often, we may need to iterate through a collection of objects (such as a list), but also keep a count of how many objects we have dealt with.

There are various ways this could be done - one might be:

a = [3, 5, 7]
for i in range(len(a)):
    print("Object number", i, " is ", a[i])

However, Python provides the enumerate function for precisely this purpose:

a = [3, 5, 7]
for i, ai in enumerate(a):
    print("Object number", i, " is ", ai)

The iterable created by enumerate returns a tuple of (index, object) pairs, which can be assigned to a pair of variables (here, i and ai).

Try the example for enumerate (above). Check for yourself for different enumerations. For example, use range(12, 24, 3) as your iterable.

# test the use of enumerate ...

zip

Another common circumstance is that we have two (or more) collections, and we want to access the \(i\)-th element of each simultaneously. Again, this could be achieved using something like:

a = [3, 5, 7]
b = ['a', 'b', 'c']
for i in range(len(a)):
    print("The letter associated with ", a[i], " is ", b[i])

but Python also provides the zip function for this purpose:

a = [3, 5, 7]
b = ['a', 'b', 'c']
for ai, bi in zip(a, b):
    print("The letter associated with ", ai, " is ", bi)

This creates an iterator which returns a tuple on each iteration, containing the \(i\)-th element of each of the collections passed to zip. The elements of this tuple can be assigned to variables (here, ai and bi).

ip may be a little bit hard to understand unless you try it (and try, try, try again). Try the example and mess around until it sinks in

# test the use of zip ...

list comprehension loops

Finally, a for loop can be used to initialise a list. This is quite a common occurence in python code you may find online, but is hard to understand when you first see it. You will often see the following shorthand:

<list> = [ <expression involving variable> for <variable> in <collection> ]

e.g.

a = [i**2 for i in range(3, 13, 2)]

This is sometimes known as a list comprehension. A tuple can be built similarly

a = tuple(i**2 for i in range(3, 13, 2))

and

a = {x:y for x, y in zip(['a', 'b', 'c'], [1, 2, 3])}

can be used to construct a dictionary.

Try the examples above … they really do work even if they look quite hard to follow. Maybe you don’t feel confident writing code like this (in which case - don’t, it’s not important to do fancy things) but it is important to be able to understand what is done if you come across this idiomatic code.

Prime numbers

The Sieve of Eratosthenes is a method for finding prime numbers (i.e., integers which are not divisible by any other integers except themselves and one). Suppose you want to find all the prime numbers less than or equal to 25. First, you write down a list of all the numbers from 2 up to 25:

2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

Now, you start crossing out numbers. First, we consider the first entry in the list, 2. We keep this, but cross out every second number thereafter:

2 3 X 5 X 7 X 9 X 11 X 13 X 15 X 17 X 19 X 21 X 23 X 25
^Start here

We move our pointer to the next not-crossed-out number, 3. We then cross out every 3rd entry (counting ones that are already crossed out):

2 3 X 5 X 7 X X X 11 X 13 X X X 17 X 19 X X X 23 X 25
  ^Start here

The next not-crossed out number is 5, so we remove every 5th entry:

2 3 X 5 X 7 X X X 11 X 13 X X X 17 X 19 X X X 23 X X
      ^Start here

If we continue this procedure, we will find that we do not cross out any more numbers. The remaining numbers - 2, 3, 5, 7, 11, 13, 17, 19 and 23 - are all the primes less than (or equal to) 25.

Write a function that takes a single input, \(N\), and returns the list of all prime numbers less than \(N\). Hint: use the ‘sieve of Eratosthenes’ that we just told you about ! Also: use a for loop !


Sandbox