7.2. Loops#

Loops allow us to execute repeating, or looping lines of code. Loops are foundational in many programming languages. In fact, they’re an example of a control flow statement, which is a computer science term for a type of instruction to the computer that controls the flow through which the computer works through the code you provide. There are many ways to control the flow through a program such that it doesn’t run simply from top to bottom.

The code we’ve covered explicitly so far all runs line by line from top to bottom. For example, when we run the below cell, the computer executes the first line, then the second line, returns the result, and then stops.

from statistics import sqrt
sqrt(144)
12.0

Sometimes, however, we may want to run a particular line or lines of code multiple times – without having to write that same code over and over. Loops to the rescue!

For loops#

One common loop we’ll use is a for loop. A for loop iterates over a particular sequence a given number of times. They are useful when we already know the number of times we wish to execute a particular line of code over and over. Here’s a simple example:

my_list = [1, 2, 3, 4]

for i in my_list:
    print(i) 
1
2
3
4

Even without breaking down the components formally, we can see that we are first creating an object called mylist (old hat to us by now, of course!). In the second line above, we are telling the computer to iterate through every item (i) in mylist, and then in the third line we’re instructing it to print each item. Let’s break down the specific components:

  1. for tells the computer we are creating a for loop.

  2. iterating_variable is the item on which we want to iterate. In the example above, we are iterating over a variable called i, but we could call it something else, such as x or item_in_list, depending on our work. Note that the notation i is most common, and one you’ll likely see in others’ code online as a matter of convention.

  3. in specifies where to look for the items over which we want to iterate

  4. sequence is the specific object over which we want to iterate; in this case, my_list

  5. We need a : at the end of the first line, just like we did when we defined a function

  6. do something is where we provide the actual action we want to computer to take over every item

As with many techniques in programming, it can almost be more confusing to work through each piece of syntax (as above), than just look at some examples and try them out. Here are a few more. As always, practice these alongside us, experiment with modifying them, and try writing your own.

Examples of for loops#

fruit_basket = ['apples', 'bananas', 'pears', 'oranges']          # another stunningly fascinating example!

for i in fruit_basket:
    print(i)
apples
bananas
pears
oranges
for x in fruit_basket:          # as stated above, we don't have to use 'i'
    print(x)
apples
bananas
pears
oranges
for fruit in fruit_basket:     # we can call it something more creative -- within the bounds of Python rules we've
    print(fruit)               # discussed; also note the () after print are required (try it without on your own)
apples
bananas
pears
oranges
import numpy as np
my_array = np.array([1, 2, 3, 4])     # we can use a Numpy array as well

for i in my_array:
    print(2*my_array)                 # we can ask the computer to do something more interesting than simply print
[2 4 6 8]
[2 4 6 8]
[2 4 6 8]
[2 4 6 8]
for i in my_list:
    print(2*my_list)                 # notice this operates like we saw back in ch. 3 when we multiplied a list
[1, 2, 3, 4, 1, 2, 3, 4]
[1, 2, 3, 4, 1, 2, 3, 4]
[1, 2, 3, 4, 1, 2, 3, 4]
[1, 2, 3, 4, 1, 2, 3, 4]

Ranges#

A range is an additional technique that you’ll see used in for loops quite a bit, but let’s consider ranges on their own before folding them into for loops.

A range creates a sequence of numbers for us so that we don’t have to type them out manually. We’ll use Numpy ranges, which use the syntax np.arange() and create Numpy arrays that contain values according to our instructions. Consider the below example:

my_range = np.arange(10)       
print(my_range)
[0 1 2 3 4 5 6 7 8 9]

In the above cell, we created a sequence called my_range that contains the integers \(0, 1, 2, 3, 4, 5, 6, 7, 8, 9\). When np.arange() is given only one argument, that argument indicates the non-inclusive ending point for that range. Thus, if we wanted my_range to include the number \(10\), we’d need to write it as follows:

my_range = np.arange(11)
print(my_range)
[ 0  1  2  3  4  5  6  7  8  9 10]

Suppose we want to start with \(1\) instead of \(0\). If we include two arguments in our range, the first argument will indicate the (inclusive) starting point and the second argument will indicate the (still non-inclusive) ending point:

my_range = np.arange(1, 11)
print(my_range)
[ 1  2  3  4  5  6  7  8  9 10]

Finally, suppose we want to only create a range with even numbers. If we include three arguments in our range, the first will still be the (inclusive) starting point, the second will still be the (non-inclusive) ending point, and the third will be the interval:

my_range = np.arange(2, 11, 2)     # notice also to get even numbers we need to change the starting point to 2
print(my_range)                    # if you're not sure why, try it on your own with 1. What happens?
[ 2  4  6  8 10]

We use ranges a lot in data science. While generally memorization is not required, if you’re going to memorize something, a good bit to hang on to is that the arguments in Numpy ranges go in this order: (start, stop, interval). (You may also see it as start, stop, step).

Ranges in for loops#

Let’s combine our powers. In the below code, we can quickly print the integers 0 through 9 by including range in our for loop alongside the usual for loop syntax. Notice that we are not using a Numpy range in this context.

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

The same start, stop, interval logic applies here, too:

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

Of course, we can again make the instructions more interesting than just print(i). For example:

for i in range(2, 10, 2):
    print(2*i)
4
8
12
16
for i in range(2, 10, 2):
    print('apple')
apple
apple
apple
apple

In the first example immediately above, we simply multiplied each element over which we iterated by \(2\). In the second example, we printed the word 'apple' for every element. (Ok, we’ll admit this still isn’t that interesting, but make sure you’re clear on all this – it will all get very interesting (maybe too interesting, even) soon!)

While loops#

A while loop is useful when we don’t know ahead of time the exact number of iterations we want to carry out. In all of the for loop examples above, we specified the ending point – for example, we said do something for each element in our object that we defined (e.g., for each element in a four-element list).

A while loop executes a block of code until a given condition is false. Here’s a simple example:

x = 0
while x<10:
    print(x)
    x = x+1
0
1
2
3
4
5
6
7
8
9

The syntax for a while loop is similar to a for loop:

  1. while tells the computer we are initiating a while loop.

  2. We then provide some expression for the computer to evaluate; as long as the expression is True, the computer will continue to carry out the code in the next line

  3. We must end our while statement with a :

  4. We then indicate the computer should do_something; in the example above, we’re simply asking it to print() (in this case, the value for \(x\))

You’ll notice a fourth line in our example above. In this particular case, we are also providing a formula instructing the computer to add the value of \(1\) to each \(x\) as we iterate. If we don’t do that, we will generate an infinite loop. To see why, let’s break down the logic of this loop in human terms:

  • First we set \(x\) equal to zero

  • Then we said, ok, as long as \(x<10\), we want you to print the value for \(x\)

  • Each time you do that, add 1 to \(x\)

  • Do this again and again until the value of \(x\) is no longer less than \(10\) (i.e., until \(x<10\) is False).

If we did not ask the computer to add \(1\), the value for \(x\) would stay \(= 0\), and our condition would never be False, and thus the computer will continue looping until your program crashes, your computer dies, or Earth succumbs to heat death from the expanding sun – whichever comes first.

Examples of while loops#

Here are a few more examples of while loops. We definitely don’t need to say this by now, but just in case: make sure you’re writing out these as well as some of your own while loops, not just reading our examples. Just like you can’t learn to speak Spanish without … speaking Spanish, you need to write code to … write (and, frankly, better understand what’s going on in) code. Yes, reading typically doesn’t hurt, but it won’t likely get you where you need to be when you’re trying to order coffee in Madrid.

x = 5                   # this while loop is the same as above, but with a different starting value for x
while x<10:
    print(x)
    x = x+1
5
6
7
8
9
x = 5
while x<=10:          # now we are slightly modifying the condition to include an additional value
    print(x)
    x = x+1
5
6
7
8
9
10
x = 5
while x==10:        # this doesn't return anything because the condition is False from the start
    print(x)        # but notice it doesn't return an error -- there's nothing syntactically incorrect here
    x = x+1
x = 5
while x==5:
    print(x)     
    x = x+1         # this only executes once because when we add 1, the condition becomes False
5
x = 0
while x<5:
    print('The value of x is', x)     # we can do a bit more than just print x!
    x = x+1
The value of x is 0
The value of x is 1
The value of x is 2
The value of x is 3
The value of x is 4
x = 0
while x<5:
    print('The value of x is', x)
    x = x+1
print('The end')                      # this only executes once because it's outside the loop
The value of x is 0
The value of x is 1
The value of x is 2
The value of x is 3
The value of x is 4
The end

Infinite loops#

Previously, we mentioned infinite loops as something to avoid. But it’s worth noting that infinite loops are not all bad. In fact, they can be kind of fun to create. And, they can be useful: we can use them to evaluate computing power or battery of machines, or to keep a machine running for long periods of time, among other uses. More often, however, we create them by accident and often won’t even realize it until we go to run our code and notice it seems to be taking an extremely long time for what we’re asking the computer to do. If this happens (and it will!), you’ll need to manually stop your code. If you’re working in a Jupyter notebook, you can do this by clicking the “stop” button (the black square to the right of Run) in the ribbon at the top of the screen, or go to Kernel > Interrupt Kernel in the menu bar.

Nested loops#

Our final loop technique we’ll consider are nested loops, which are exactly what they sound like: loops inside of loops. As ever, these can seem a big confusing to read at first, but writing them out should help you see the logic for how they work. Here’s an example:

for i in range(1, 10):
    for j in range (1, 2):            # notice we're using j instead of i. What happens if you use i twice?
         print(i+j)                   
2
3
4
5
6
7
8
9
10
x = 5
while x < 10:                        
    for i in range(1, 5):            # we can create nested loops with different kinds of loops
        print(x*i)
        x = x+2
5
14
27
44

The above example might be a little bit tricky to think through at first, but we can go step by step in human terms: we’re starting by assigning the value 5 to \(x\). We then set up our first loop, a while loop, that says to execute the instructions that follow for as long as the condition of \(x<10\) is true. The instructions, then, are to consider every element \(i\) in a range that includes 1, 2, 3, and 4 (remember, the stopping point is non-inclusive), and then print the value of \(x\) multiplied by that \(i\). Then add two to \(x\) and do the whole thing from the beginning of the while loop to the end as long as the value of \(x\) continues to be less than 10. Whew – that’s a lot of words!

Thus – we see the printed resultd are 5, 14, 27, and 44, because:

  • \(5*1 = 5\)

  • \(7*2 = 10\)

  • \(9*3 = 27\)

  • \(11*4 = 44\)

As always, make sure you’re practicing with these, as well as creating your own, so the intuition around how these work comes more easily. Your future coding selves will thank you!