7  Loops

Jupyter Notebook

Loops allow programs to rerun the same block of code multiple times. This might seem like a funny thing to want to do but it turns out that there are many important tasks that are repetitive in nature (perhaps with small changes for each successive repetition). A loop provides a succinct and efficient way to perform tasks of this nature.

7.1 for loops

The for loop is probably the most common loop you will encounter and is a good choice when you know beforehand exactly what things you want to loop over. Here is an example of a for loop that is used to add up the elements of a list.

thesum = 0
for i in [3,2,1,9.9]: 
    thesum += i

This would be equivalent to the following code:

::: {.cell execution_count=2} ``` {.python .cell-code} thesum = 0

thesum = thesum + 3 thesum = thesum + 2 thesum = thesum + 1 thesum = thesum + 9.9

:::


which isn't that much longer than using a loop.  However, as the list gets longer and/or the mathematical operations being performed get more complex the second method would get unreasonably long.


The correct language is to say that we are _iterating_ over the list `[3,2,1,9.9]`.  This
means that the loop variable (`i` in this case but you can choose it to be whatever you want) gets assigned
the values of the list elements, one by one, until it reaches the end
of the list.  You can use `for` loops to iterate over any multi-element object like lists or tuples.  Python uses indentation to indicate
where the loop ends. In this case there was only one statement inside to loop, but if you wanted more than one each line should be indented.  


>**_To Do:_**
>
>1. Add a `print` statement inside of the loop above to display the value of the variable `thesum`.
>2. Predict what the output will be and then run the code and verify that you were correct.
>3. Now change the loop variable to be named `physics` and verify that the loop still works as expected.


You can iterate over `range` objects and strings using `for` loops.

::: {.cell execution_count=3}
``` {.python .cell-code}
for i in ['Physics', 'is','so','fun']: # Iterate over a list of strings
   print(i)
Physics
is
so
fun

:::

for i in range(5,50,3):  #Generates a list from 5 -> 50 with a step size of 3
    print(i)
5
8
11
14
17
20
23
26
29
32
35
38
41
44
47

These examples are so simple that you might wonder when a loop might actually be useful to you. Let’s see if we can build a loop to calculate the following sum

\[ \sum_{n=1}^{1000} {1\over n^2} \tag{7.1}\]

theSum = 0
for n in range(1,1000):
    theSum = theSum + 1/n**2
print(theSum)
1.6439335666815615

Here, n is being assigned the values 1,2,3,4....1000, one by one, until it gets all the way to 1000. Each time through the loop, n is different and the expression 1/n**2 evaluates to a new value. The variable theSum is updated each time through to be the running total of all calculations performed thus far. Here’s another example of a loop used to calculate the value of \(20!\):

theProduct = 1
for n in range(1,21):
    theProduct = theProduct * n #Multiply theProduct by n
print(theProduct)
2432902008176640000

Remember that the range function creates a list starting at \(1\), going up to \(21\) but not including it. The math library has a function called factorial that does the same thing. Let’s use it to check our answer:

from math import factorial
factorial(20)
2432902008176640000

7.1.1 Boolean Logic Inside Loops

Often when using loops, we only want a block of code to execute when some condition is satisfied. We can use boolean logic inside of the loop to accomplish this. For example, let’s write a loop to compute the following sum:

\[ \sum_{{n\over 5} \in \text{ Int and } {n\over 3} \in \text{ Int}} {1\over n^2} \]

which is similar to the one we did above, but this time we only want to include terms where \(n\) is a perfect multiple of both 5 and 3. To check to see if n is a perfect multiple of a number we can calculate the modulo (remainder after division) using the % operator and check that it is equal to zero.

theSum = 0
for n in range(1,1000):
    if n % 5 == 0 and n % 3 == 0:
        theSum = theSum + 1/n**2
print(theSum)
0.007243985583159138

To Do:

Perform the following modifications to the loop above.

  1. Increase the upper bound of the sum to go up to and include \(5000\).
  2. Only include the terms where n is a multiple of 5 and 3 or is a multiple of 7.
  3. Replace the statement that updates theSum to its shorthand version.

7.1.2 Zipping and Enumerating

There are times when it is necessary to iterate over two lists simultaneously. For example, let us say that we have a list of atomic numbers (AN) and a list of approximate atomic masses (mass) of the most abundant isotopes for the first six elements on the periodic table.

AN = [1,2,3,4,5,6]
mass = [1,4,7,9,11,12]

If we want to calculate the number of neutrons in each isotope, we need to subtract each atomic number from the atomic mass. To accomplish this, it would be nice to iterate over both lists simultaneously

7.1.2.1 Zipping

The simplest way to iterate over two lists simultaneously is to combine both lists into a single, iterable object and iterate over it once. The zip function does just that by merging two lists or tuples into a nested list

AN = [1,2,3,4,5,6]
mass = [1,4,7,9,11,12]

zipped = zip(AN,mass)

for pair in zipped:
    print(pair[1] - pair[0])
0
2
4
5
6
6

The zip objects are “single use” so you can’t reuse zipped in a later loop. If the two lists being zipped are not the same length, zip stops zipping when it reaches the end of the shorter list.

To Do:

  1. Print the variable zipped and inspect closely. Was the output what you expected?
  2. Now do print(tuple(zipped)) and inspect closely. Draw a conclusion.
  3. Add a few more entries to the list named AN, but don’t add the corresponding entries to the other list. Now the lists being zipped aren’t the same length. Inspect the output of the print statement to determine what zip does in this scenario.

7.1.2.2 Enumeration

A close relative to zip is enumerate which zips a list to the index value for that list (read that last statement again). It also returns a “single use” object that can be iterated over.

AN = [1,2,3,4,5,6]
mass = [1,4,7,9,11,12]

enum = enumerate(mass)

for idx,val in enum:
    print(val - AN[idx])
0
2
4
5
6
6

To Do:

  1. Repeat the previous To-Dos for the cell above.

7.1.3 List Comprehension

It is fairly common to use a for loop to populate a list with a sequence of numbers.

myList = []

for i in range(10):
    myList.append(i**2)

print(myList)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

This entire process can be compressed down into a single line by expressing the for loop in square brackets. This is known as list comprehension. The code below will generate the list as above.

myList = [i**2 for i in range(10)]
print(myList)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

List comprehension can take a little time to get used to but it is well worth it. It saves both time and space and makes code less cluttered. You can even add boolean expressions to your conditionals for further control of the final result.

myList = [i**2 for i in range(10) if i %2 == 0]  # Include only the evens.
print(myList)
[0, 4, 16, 36, 64]

To Do:

Think of your favorite mathematical function and modify the code above to generate a few samples from it.

7.2 while Loops

Logic can be combined with loops using something called a while loop. A while loop is a good choice when you don’t know beforehand exactly how many iterations of the loop will be executed but rather want the loop to continue to execute until some condition is met. As an example, notice that in Equation 7.1, the terms in the sum get progressively smaller as \(n\) gets bigger. It doesn’t make sense to continue adding to the sum once the terms get very small. Let’s compute this sum by looping until the fraction \({1 \over n^2}\) become smaller than \(1 \times 10^{-2}\).

term = 1  # Load the first term in the sum
s = term  # Initialize the sum
n = 1     # Set a counter
while term > 1e-2:  # Loop while term is bigger than 1e-2
    n = n +  1        #Add 1 to n so that it will count: 2,3,4,5
    term = 1./n**2    # Calculate the next term to add
    s = s +  term     # Add 1/n^2 to the running total

This loop will continue to execute until term<1e-2. Note that unlike the for loop, here you have to do your own counting if you need to know how many iterations have been performed. Be careful about what value n starts at and when it is incremented (n = n + 1). Also notice that term must be assigned prior to the start of the loop. If it wasn’t the loop’s first logical test would fail and the loop wouldn’t execute at all.

To Do:

  1. Decrease the threshold on the termination condition and observe any changes in the final result and how many more iterations are performed.
  2. After toying around with it for a while pick a termination condition that you feel will produce a result that is accurate.

while loops should be used with caution because you can easily write a faulty termination condition and inadvertently write a loop that runs forever. This happens because your termination condition was never met. An example of this is given below.

Warning: Do not execute the code block below!!

x = 0

while x != 10:
    x = x + 3
print("Done")

The loop above is intended to end after a few iterations when the value of x is equal to 10. However, closer inspection reveals that the value of x will never be equal to 10. After the first iteration x is equal to 3, then 6,9,12,15 and so on… but never 10. This loop will run forever because the termination condition is never met (x != 10 never produces a False)!! If you choose to use a while loop, triple check your termination condition to make sure you haven’t made a mental error. Avoiding the use of != or == in your termination condition will help too. Use <= or >= instead.

To Do:

  1. Modify the termination condition in the loop above so that it terminates when x gets larger than 15.
  2. Run the code and verify that you did it correct.

7.3 continue, break, and pass Commands

The continue, break, and pass commands are used to control the flow of code execution in loops. Here is a description of their usage:

Operator Description
break Exits a for/while loop.
continue Skips the remaining loop block and begins the next iteration.
pass No action; code contiues on

The break statement is useful when you want to completely stop a loop early. Here is our sum loop rewritten with a break statement added to stop the loop after 1000 iterations.

term = 1  # Load the first term in the sum
s = term  # Initialize the sum
n = 1     # Set a counter
while term > 1e-10:  # Loop while term is bigger than 1e-10
    n +=  1        #Add 1 to n so that it will count: 2,3,4,5
    term = 1./n**2    # Calculate the next term to add
    s += term     # Add 1/n^2 to the running total
    if n > 1000:
        print('This is taking too long. I''m outta here...')
        break
This is taking too long. Im outta here...

The continue statement is similar to break except that instead of stopping the loop, it only stops the current iteration of the loop. All code below the continue statement will be skipped and the next iteration will begin. For example, if you wanted to do the sum from equation ?? but only include those terms for which n is a multiple of 3, it could be done like this:

term = 1  # Load the first term in the sum
s = term  # Initialize the sum
n = 1     # Set a counter
while term > 1e-10:  # Loop while term is bigger than 1e-10
    n +=  1        #Add 1 to n so that it will count: 2,3,4,5
    if n % 3 != 0:
        continue
    term = 1./n**2    # Calculate the next term to add
    s += term     # Add 1/n^2 to the running total

Now, when the value of n is not a multiple of 3, the sum will not be updated and the associated terms are effectively skipped.

Finally, the pass statement does nothing. Seriously!! It is merely a place holder for code that has not bee written yet. Usually, you’ll use the pass statement to run and test code without errors due to missing code.

7.4 Flash Cards

  1. Explain the basic structure and key elements of a for loop.
  2. Explain the basic structure and key elements of a while loop.
  3. What does the break statement do?
  4. What does the continue statement do?
  5. What does the pass statement do?
  6. What does the zip function do? Give an example.
  7. What does the enumerate function do? Give an example.
  8. What is list comprehension? Give an example.
  9. Where is the account of Jesus walking on the water?

7.5 Exercises

  1. Use a loop to construct a list containing the first 10 fibonacci numbers that are multiples of \(5\). (The modulo operator (%) is a good and efficient way to see if a number is a multiple of another.)

Hint/Answer: The 10th fibonacci number that is a multiple of 5 is: 12,586,269,025

# Python code here.
  1. Summations appear often in science and mathematics. One such summation is called the Riemann Zeta function and is given by \[ \zeta(n) = \sum_{k=1}^\infty {1\over k^n} = {1\over 1^n} + {1\over 2^n} + {1\over 3^n} + \dots\]

    1. Use a for loop to evaluate \(\zeta(2)\) and \(\zeta(4)\) and verify that \(\zeta(2) = {\pi^2 \over 6}\) and \(\zeta(4) = {\pi^4 \over 90}\). This summation is called an infinite sum and we surely don’t want our loop to continue forever!! Instead, an infinite sum like this one can be approximated with a for loop with a large number of iterations. To determine if you have included enough terms, increase the number of terms steadily and watch for the final result to stop changing appreciably.
    2. Now repeat part 1 using list comprehension and the sum function?
    3. Repeat the exercise one final time using a while loop. Write the while loop so that it stops iterating when the value of \({1\over k^n}\) gets smaller than \(1 \times 10^{-6}\).
#Python code here
  1. In the cell below you will find three lists containing the masses, lengths, and radii for a collection of cylinders. The moment of inertia for each cylinder can be calculated as \[ I = {1\over 4} M R^2 + {1\over 12} M L^2\].

    1. Use a loop to iterate over these lists and calculate the moment of inertia for each one. Store the calculated values in a new list. You should use the enumerate function on this problem.
    2. Calculate the largest, smallest, and average of the calculated values
from numpy.random import uniform
mass = uniform(3,8,1000)
radius = uniform(0.5,1.2,1000)
length = uniform(0.8,3,1000)
  1. An object that is dropped/thrown from a high altitude and allowed to fall through the air will experience a drag force that is opposite its motion. Because of that drag force, the velocity of the particle will not increase forever, but instead approach a constant velocity (called terminal velocity). The function that describes the velocity of the particle as a function of time is given by \[ v(t) = -{mg \over c} + \left( {m g \over c} + v_0\right) e^{-c t \over m}\] and the terminal velocity is given by \[v_t = -{m g \over c}\] where \(m\) is the mass of the projectile, \(c\) is the drag constant, \(g= 9.8\) m/s\(^2\) is the acceleration due to gravity, \(v_0\) is the initial velocity of the particle, and \(t\) is time.

    1. Use a while loop to calculate the velocity of the particle at \(0.1\) second time intervals, starting at \(t = 0\).
    2. The loop should terminate when the velocity of the particle achieves \(90\%\) of terminal velocity.
    3. Use a print statement to display the amount of time that has elapsed before reaching \(90\%\) of terminal velocity.
    4. Hypothesize about what might happen if you increased or decreased the mass of the object, while leaving all other physical parameters fixed. Then modify your code and see if your guess was right.
    5. Hypothesize about what might happen if you increased or decreased the drag constant (c) for the object, while leaving all other physical parameters fixed. Then modify your code and see if your guess was right.
from math import exp,sqrt
m = 20
g = 9.8
c = 0.75
v0=0
  1. In the cell below you will find a dictionary containing the mass(in kg) and diameter of the planets in our solar system. Use a loop to iterate over this dictionary and calculate the acceleration due to gravity at the surface of the planet. \[ g = {G M \over R^2}\]. Then add this value to the list for each planet. Print the dictionary when you are done to verify that each dictionary entry is a list with three elements.
planetData = {"Mercury": [0.33e24,4.879e6],"Venus":[4.87e24,1.2104e7],"Earth":[5.97e24,1.2756e7],"Mars":[0.642e24,6.792e6],"Jupiter":[1.898e27,1.42984e8],"Saturn":[5.68e26,1.20536e8],"Uranus":[8.68e25,5.1118e7],"Neptune":[1.02e26,4.9528e7],"Pluto":[0.013e24,2.376e7]}

(Extra Credit) A transcendental equation is one that cannot be solved analytically (with pencil and paper). Try solving the following equation for x to see what I mean: \[ {\sin x \over x} = 1\] One numerical method for solving an equation like this involves first rearranging it to look like this: \[ x = \sin x\] and then using a loop to repeatedly evaluating the right hand side, using the result of the previous evaluation as the input, until subsequent evaluations differ very little (\(1 \times 10^{-3}\) would be fine). 1 The first few iterations of this process might look something like this: \[(x_\text{old})_1 = 2 \text{ (starting guess)}\] \[(x_\text{new})_1 = \sin((x_\text{old})_1)\] \[(x_\text{old})_2 = (x_\text{new})_1\] \[(x_\text{new})_2 = \sin((x_\text{old})_2)\] \[(x_\text{old})_3 = (x_\text{new})_2\] \[(x_\text{new})_3 = \sin((x_\text{old})_3)\]

  • 1 This is called successive relaxation.

    1. Use a while loop to solve this equation and report the correct value for x.

    2. Plug the answer back into the equation to verify that it indeed does satisfy the equation.

    3. Report how many iterations it took to solve the problem.

    4. Decrease the value in the termination condition and observe how the number of iterations and the accuracy of the answer change.

      Hint: You’ll have to define two variables: one to hold the old guess and one to hold the updated guess. Initialize these variables to any old values but make sure that they aren’t equal or your stopping criteria will trigger on the first iteration and the loop won’t run at all.

    xold = 2 # starting guess