Introduction to Python Programming. Section 11. The ’While’ Loop

11 The ’While’ Loop

11.1 Objectives

The while loop was mentioned in various contexts in Subsections 6.3, 6.13, 7.19, 9.10, 9.11 and 9.12. It is very similar to the for loop that you already know from Section 9, with one main difference. In other words, there is only one thing you need to learn about the while loop. In this section we will show you:

  • The main difference between the for and while loops.
  • How to terminate a while loop with a break statement.
  • How to measure the execution time of your programs.
  • How to use the continue statement to skip the rest of a while loop.
  • How to emulate the do-while loop which Python does not have.
  • How to use the while loop to solve an equation that cannot be solved on paper.

11.2 Why learn about the while loop?

Every imperative programming language has two types of loops that serve different purposes: The for (counting) loop is used when the number of cycles is known in advance. Such as, when one needs to parse all items in a list, or when one just needs to repeat something 10 times.

On the contrary, the while (conditional) loop is open-ended. Instead of a number of repetitions, it has a condition and keeps going while the condition is satisfied.

11.3 Syntax and examples

The while loop in Python has the following general format:

  while <boolean_expression>:
      <do_something>

There is a mandatory indent which is the same as for the for loop, conditions, and functions. Moreover, there is a mandatory colon : after the Boolean expression. The while loop can accept the same types of Boolean expressions as conditions. Let’s begin with the Boolean values True,

  while True:
      print(’This will be printed infinitely many times.’)

and False,

  while False:
      print(’This will never be printed.’)

Both these cases are actually useful in practise – the former in combination with the break statement and the later to temporarily disable code, for example for debugging purposes.

Let’s do one more example, now with an inequality (Boolean expression). Did you know that when adding 1/1, 1/2, 1/3, 1/4, 1/5 etc. one can eventually exceed any real number? Let’s do this: For a given real number N we will be adding these fractions in a loop until their sum exceeds N, and we’ll count how many fractions we needed:

  N = 10
  c = 0
  total = 0
  while total < N:
      c += 1
      total += 1 / c
  print(’Total =’, total)
  print(’c =’, c)

  Total = 10.000043008275778
  c = 12367

Do not choose N too high though – already with N = 20 the computer will work for several minutes.

11.4 How to time your programs

Measuring execution time of functions and programs is a bit more tricky than it appears. The reason is that there are other processes running on your computer. If you measure the wall time (time on your clock), then of course the time taken by these processes is included.

The wall time can be measured using the built-in function time from the time library. Just calling time.time() is not very helpful because it returns the total time since the beginning of the epoch:

  import time
  print(time.time())

  1532160646.6236913

However, when called once at the beginning and then at the end of the program, it yields its exact duration in seconds (wall time). For illustration, let’s measure the execution time of the program from Subsection 11.3:

  import time
  N = 18
  c = 0
  total = 0
  start = time.time()
  while total < N:
      c += 1
      total += 1 / c
  print(’Total =’, total)
  print(’c =’, c)
  print(’Computation took’, time.time() - start, ’s.’)

  Total = 18.00000000371793
  c = 36865412
  Computation took 13.102287769317627 s.

If you are interested in measuring the time the CPU spends with just your function or program (and not other processes), use the function process_time instead:

  import time
  N = 18
  c = 0
  total = 0
  start = time.process_time()
  while total < N:
      c += 1
      total += 1 / c
  print(’Total =’, total)
  print(’c =’, c)
  print(’Computation took’, time.process_time() - start, ’s.’)

  Total = 18.00000000371793
  c = 36865412
  Computation took 12.925883286 s.

As you can see, the process time is slightly shorter than the wall time.

And finally - the result of the timing will be slightly different every time your program is executed. Therefore, to have a realistic understanding of how much time your program really takes, run it multiple times.

11.5 The break statement

You already saw the break statement in Subsection 9.10 where we used it to terminate the for loop. It can be used to exit a while loop at any time too, no questions asked. If multiple loops are embedded, then it only exits the closest outer one. The break statement can be used, for example, as an emergency brake to prevent a while loop from running forever. The computation length in the previous example starts to be a problem with N around 20. Therefore, let’s set the timeout to 30 seconds (wall time):

  import time
  N = 20
  c = 0
  total = 0
  timeout = 30
  start = time.time()
  while total < N:
      c += 1
      total += 1 / c
      if time.time() - start > timeout:
          print(’Computation took too long, exiting.’)
          break
  print(’Total =’, total)
  print(’c =’, c)

  Computation took too long, exiting.
  Total = 18.63800087724099
  c = 69774921

11.6 The continue statement

The continue statement which you learned for the for loop in Subsection 9.11 works for the while loop analogously – it skips the rest of the current cycle and begins with the next one.

For illustration, let us write a program to generate triplets of random numbers between zero and one until all three of them are greater than 0.9. We also want to know how many attempts were needed. Clearly, if the first random number is less than or equal to 0.9, it does not make sense to generate the other two. That’s when we use continue. Then again, if the second random number is less than or equal to 0.9, it is time to use continue. At the end, when all three random numbers are above 0.9, we use break to terminate the loop. Here is the code:

  import random as rn
  counter = 0
  while True:
      counter += 1
      a1 = rn.random()
      if a1 <= 0.9:
          continue
      a2 = rn.random()
      if a2 <= 0.9:
          continue
      a3 = rn.random()
      if a3 <= 0.9:
          continue
      print(~Finally the desired triplet:~)
      print(a1, a2, a3)
      print(~It took~, counter, ~attempts to get it!~)
      break

Note that while True was used here to create an infinite loop. Sample output:

Finally the desired triplet:  
0.921077479893 0.956493808495 0.917136354634  
It took 1168 attempts to get it!

11.7 Emulating the do-while loop

In practice one often needs to perform some action once before repeating it in the while loop. Therefore some languages (C, C++, Java, ...) have the do-while loop. Python does not have it because it is easy to emulate – we are going to show you how.

As an example, let’s say that we need to generate random numbers between zero and one as long as they are less than 0.75. When the number is greater or equal to 0.75, the loop will stop. First, here is a cumbersome way to do this using the standard while loop. Notice that the code for generating random numbers is there twice:

  import random as rn
  a = rn.random()
  while a < 0.75:
      print(a)
      a = rn.random()

A popular way to circumvent this problem and only have the essential code in the loop once, is to use an infinite loop combined with a break statement:

  import random as rn
  while True:
      a = rn.random()
      print(a)
      if a >= 0.75:
          break

Sample output:

0.383771890647  
0.068637471541  
0.0850385942776  
0.682442836322  
0.168282761575  
0.121694025066  
0.185099163429  
0.606740825997  
0.209771333501  
0.186191737708

11.8 The while-else statement

The while loop can be enhanced with a completion (nobreak) clause else in the same way as the for loop (see Subsection 9.12). The else branch is executed always, even when the while loop does not perform a single cycle:

  while False:
      pass
  else:
      print(’While loop finished without break.’)
  print(’Code after the while loop.’)

  While loop finished without break.
  Code after the while loop.

But importantly, it will be skipped when the loop is terminated with the break statement:

  while True:
      break
  else:
      print(’While loop finished without break.’)
  print(’Code after the while loop.’)

  Code after the while loop.

The else branch can obviously be used to acknowledge a successful completion of the while loop without the need to use the break statement. But one can also designate break to be the successful ending, and then the else branch can be used for an error message. This is illustrated on the following example where the user is asked to keep entering positive integers, until s/he enters ’x’ to finish:

  L = []
  a = ’0’
  while a.isdigit():
      a = input(~Enter a positive integer, or ’x’ to finish:~)
      if a == ’x’:
          break
      if a.isdigit():
          L.append(int(a))
  else:
      raise Exception(~You should have entered positive integers
      or ’x’!\nSelf-destructing in 60 seconds...~)
  print(’Thank you, you entered’, L)

  Exception: You should have entered positive integers or ’x’!
  Self-destructing in 60 seconds...

11.9 Abusing the while loop

Codes like this have seen the light of the world too many times:

  counter = 0
  while counter < 20:
      # Do something with the counter, here
      # for simplicity we just print it:
      print(counter)
      # Increase the counter:
      counter += 1

Although there is nothing syntactically wrong, a person who writes such a code probably does not understand why programming languages have two types of loops. A better code for the same would be:

  for counter in range(20):
      # Do something with the counter, here
      # for simplicity we just print it:
      print(counter)

11.10 Solving an unsolvable equation

As your programming skills get stronger, you can tackle more difficult problems. In this subsection we want to demostrate what scientific computing is all about, by solving an equation that cannot be solved "on paper". The task is to find an angle x that is equal to its own cosine. In other words, we need to solve the equation

cos(x) = x.
As the first step, let us visualize the graphs so that we know where approximately the solution can be expected:

  import numpy as np
  import matplotlib.pyplot as plt
  x = np.linspace(0, np.pi/2, 100)
  y = np.cos(x)
  z = x
  plt.clf()
  plt.plot(x, y, label=cos(x))
  plt.plot(x, z, label=x)
  plt.legend()
  plt.show()

The output is shown in Fig. 53.


PIC


Fig. 53: Graphs of the functions cos(x) and x in the interval (0,π∕2).


The solution that we are after is the value of x where the two graphs intersect. Looking at the picture, clearly the solution is somewhere between 0.6 and 0.8. An engineer would not be extremely happy with such an answer though – we need to make this guess much more accurate.

Looking at the graphs again, we can see that in order to reach the intersection point, we can depart from zero and march with very small steps to the right while the value of cos(x) is greater than the value of x. We stop as soon as cos(x) becomes less than x. Clearly, the while loop needs to be used as we do not know exactly how many steps will be taken.

Let’s do this. Our step will be called dx, and our marching point on the x-axis will be called simply x. We will also count steps via the variable n. Choosing dx = 1e-6 will give us the result accurate to five decimal digits:

  import numpy as np
  x = 0
  dx = 1e-6
  n = 0
  while np.cos(x) > x:
      x += dx
      n += 1
  print(Result is approximately, x)
  print(Steps made:, n)

Result is approximately 0.739086  
Steps made: 739086

In other words, the while loop ran 739086 times!

11.11 Numerical methods and scientific computing

Note that the numerical method that we used was rather naive and took too much CPU time. Scientific computing is an exciting field where researchers develop new methods that can solve problems like this more accurately and using less CPU time. The problem we just solved belongs to a wider category of rootfinding problems. There are much more powerful methods than the one we showed. In particular, the so-called Newton’s method can solve it in just 5 steps (compare to 739086). One of NCLab’s main objectives is to ease access to scientific computing via its Creative Suite.


Table of Contents

Created on August 6, 2018 in Python I,   Python II.
Add Comment
0 Comment(s)

Your Comment

By posting your comment, you agree to the privacy policy and terms of service.