$$ \newcommand{\tp}{\thinspace .} $$

This chapter is taken from the book A Primer on Scientific Programming with Python by H. P. Langtangen, 5th edition, Springer, 2016.

While loops

Our task now is to print out a conversion table with Celsius degrees in the first column of the table and the corresponding Fahrenheit degrees in the second column. Such a table may look like this:

  -20  -4.0
  -15   5.0
  -10  14.0
   -5  23.0
    0  32.0
    5  41.0
   10  50.0
   15  59.0
   20  68.0
   25  77.0
   30  86.0
   35  95.0
   40 104.0

A naive solution

The formula for converting \( C \) degrees Celsius to \( F \) degrees Fahrenheit is \( F=9C/5 + 32 \). Since we know how to evaluate the formula for one value of \( C \), we can just repeat these statements as many times as required for the table above. Using three statements per line in the program, for compact layout of the code, we can write the whole program as

C = -20;  F = 9.0/5*C + 32;  print C, F
C = -15;  F = 9.0/5*C + 32;  print C, F
C = -10;  F = 9.0/5*C + 32;  print C, F
C =  -5;  F = 9.0/5*C + 32;  print C, F
C =   0;  F = 9.0/5*C + 32;  print C, F
C =   5;  F = 9.0/5*C + 32;  print C, F
C =  10;  F = 9.0/5*C + 32;  print C, F
C =  15;  F = 9.0/5*C + 32;  print C, F
C =  20;  F = 9.0/5*C + 32;  print C, F
C =  25;  F = 9.0/5*C + 32;  print C, F
C =  30;  F = 9.0/5*C + 32;  print C, F
C =  35;  F = 9.0/5*C + 32;  print C, F
C =  40;  F = 9.0/5*C + 32;  print C, F

Running this program (which is stored in the file c2f_table_repeat.py), demonstrates that the output becomes

-20 -4.0
-15 5.0
-10 14.0
-5 23.0
0 32.0
5 41.0
10 50.0
15 59.0
20 68.0
25 77.0
30 86.0
35 95.0
40 104.0
This output suffers from somewhat ugly formatting, but that problem can quickly be fixed by replacing print C, F by a print statement based on printf formatting. We will return to this detail later.

The main problem with the program above is that lots of statements are identical and repeated. First of all it is boring to write this sort of repeated statements, especially if we want many more \( C \) and \( F \) values in the table. Second, the idea of the computer is to automate repetition. Therefore, all computer languages have constructs to efficiently express repetition. These constructs are called loops and come in two variants in Python: while loops and for loops. Most programs in this document employ loops, so this concept is extremely important to learn.

While loops

The while loop is used to repeat a set of statements as long as a condition is true. We shall introduce this kind of loop through an example. The task is to generate the rows of the table of \( C \) and \( F \) values. The \( C \) value starts at \( -20 \) and is incremented by 5 as long as \( C\leq 40 \). For each \( C \) value we compute the corresponding \( F \) value and write out the two temperatures. In addition, we also add a line of dashes above and below the table.

The list of tasks to be done can be summarized as follows:

This is the algorithm of our programming task. The way from a detailed algorithm to a fully functioning Python code can often be made very short, which is definitely true in the present case:

print '------------------'     # table heading
C = -20                        # start value for C
dC = 5                         # increment of C in loop
while C <= 40:                 # loop heading with condition
    F = (9.0/5)*C + 32         # 1st statement inside loop
    print C, F                 # 2nd statement inside loop
    C = C + dC                 # 3rd statement inside loop
print '------------------'     # end of table line (after loop)

A very important feature of Python is now encountered: the block of statements to be executed in each pass of the while loop must be indented. In the example above the block consists of three lines, and all these lines must have exactly the same indentation. Our choice of indentation in this document is four spaces. The first statement whose indentation coincides with that of the while line marks the end of the loop and is executed after the loop has terminated. In this example this is the final print statement. You are encouraged to type in the code above in a file, indent the last line four spaces, and observe what happens (you will experience that lines in the table are separated by a line of dashes: --------).

Many novice Python programmers forget the colon at the end of the while line - this colon is essential and marks the beginning of the indented block of statements inside the loop. Later, we will see that there are many other similar program constructions in Python where there is a heading ending with a colon, followed by an indented block of statements.

Programmers need to fully understand what is going on in a program and be able to simulate the program by hand. Let us do this with the program segment above. First, we define the start value for the sequence of Celsius temperatures: C = -20. We also define the increment dC that will be added to C inside the loop. Then we enter the loop condition C <= 40. The first time C is -20, which implies that C <= 40 (equivalent to \( C\leq 40 \) in mathematical notation) is true. Since the loop condition is true, we enter the loop and execute all the indented statements. That is, we compute F corresponding to the current C value, print the temperatures, and increment C by dC. For simplicity, we have used a plain print C, F without any formatting so the columns will not be aligned, but this can easily be fixed later.

Thereafter, we enter the second pass in the loop. First we check the condition: C is -15 and C <= 40 is still true. We execute the statements in the indented loop block, C becomes -10, this is still less than or equal to 40, so we enter the loop block again. This procedure is repeated until C is updated from 40 to 45 in the final statement in the loop block. When we then test the condition, C <= 40, this condition is no longer true, and the loop is terminated. We proceed with the next statement that has the same indentation as the while statement, which is the final print statement in this example.

Newcomers to programming are sometimes confused by statements like

C = C + dC
This line looks erroneous from a mathematical viewpoint, but the statement is perfectly valid computer code, because we first evaluate the expression on the right-hand side of the equality sign and then let the variable on the left-hand side refer to the result of this evaluation. In our case, C and dC are two different int objects. The operation C+dC results in a new int object, which in the assignment C = C+dC is bound to the name C. Before this assignment, C was already bound to an int object, and this object is automatically destroyed when C is bound to a new object and there are no other names (variables) referring to this previous object (if you did not get this last point, just relax and continue reading!).

Since incrementing the value of a variable is frequently done in computer programs, there is a special short-hand notation for this and related operations:

C += dC   # equivalent to C = C + dC
C -= dC   # equivalent to C = C - dC
C *= dC   # equivalent to C = C*dC
C /= dC   # equivalent to C = C/dC

Boolean expressions

In our first example on a while loop, we worked with a condition C <= 40, which evaluates to either true or false, written as True or False in Python. Other comparisons are also useful:

C == 40    # C equals 40
C != 40    # C does not equal 40
C >= 40    # C is greater than or equal to 40
C >  40    # C is greater than 40
C <  40    # C is less than 40
Not only comparisons between numbers can be used as conditions in while loops: any expression that has a boolean (True or False) value can be used. Such expressions are known as logical or boolean expressions.

The keyword not can be inserted in front of the boolean expression to change the value from True to False or from False to True. To evaluate not C == 40, we first evaluate C == 40, for C = 1 this is False, and then not turns the value into True. On the opposite, if C == 40 is True, not C == 40 becomes False. Mathematically it is easier to read C != 40 than not C == 40, but these two boolean expressions are equivalent.

Boolean expressions can be combined with and and or to form new compound boolean expressions, as in

while x > 0 and y <= 1:
    print x, y
If cond1 and cond2 are two boolean expressions with values True or False, the compound boolean expression cond1 and cond2 is True if both cond1 and cond2 are True. On the other hand, cond1 or cond2 is True if at least one of the conditions, cond1 or cond2, is True

Remark.

In Python, cond1 and cond2 or cond1 or cond2 returns one of the operands and not just True or False values as in most other computer languages. The operands cond1 or cond2 can be expressions or objects. In case of expressions, these are first evaluated to an object before the compound boolean expression is evaluated. For example, (5+1) or -1 evaluates to 6 (the second operand is not evaluated when the first one is True), and (5+1) and -1 evaluates to -1.

Here are some more examples from an interactive session where we just evaluate the boolean expressions themselves without using them in loop conditions:

>>> x = 0;  y = 1.2
>>> x >= 0 and y < 1
False
>>> x >= 0 or y < 1
True
>>> x > 0 or y > 1
True
>>> x > 0 or not y > 1
False
>>> -1 < x <= 0   #  -1 < x and x <= 0
True
>>> not (x > 0 or y > 0)
False
In the last sample expression, not applies to the value of the boolean expression inside the parentheses: x>0 is False, y>0 is True, so the combined expression with or is True, and not turns this value to False.

The common boolean values in Python are True, False, 0 (false), and any integer different from zero (true). To see such values in action, we recommend doing Exercise 22: Interpret a code and Exercise 18: Values of boolean expressions.

Boolean evaluation of an object.

All objects in Python can in fact be evaluated in a boolean context, and all are True except False, zero numbers, and empty strings, lists, and dictionaries:

>>> s = 'some string'
>>> bool(s)
True
>>> s = ''  # empty string
>>> bool(s)
False
>>> L = [1, 4, 6]
>>> bool(L)
True
>>> L = []
>>> bool(L)
False
>>> a = 88.0
>>> bool(a)
True
>>> a = 0.0
>>> bool(a)
False
Essentially, if a tests if a is a non-empty object or if it is non-zero value. Such constructions are frequent in Python code.

Erroneous thinking about boolean expressions is one of the most common sources of errors in computer programs, so you should be careful every time you encounter a boolean expression and check that it is correctly stated.

Loop implementation of a sum

Summations frequently appear in mathematics. For instance, the sine function can be calculated as a polynomial: $$ \begin{equation} \sin (x) \approx x - \frac{x^3}{3!} + \frac{x^5}{5!} - \frac{x^7}{7!} + \cdots, \tag{1} \end{equation} $$ where \( 3!=3\cdot 2\cdot 1 \), \( 5! = 5\cdot 4\cdot 3\cdot 2\cdot 1 \), etc., are factorial expressions. Computing \( k!=k(k-1)(k-2)\cdots 2\cdot 1 \) is done by math.factorial(k).

An infinite number of terms are needed on the right-hand side of (1) for the equality sign to hold. With a finite number of terms, we obtain an approximation to \( \sin (x) \), which is well suited for being calculated in a program since only powers and the basic four arithmetic operations are involved. Say we want to compute the right-hand side of (1) for powers up to \( N=25 \). Writing out and implementing each one of these terms is a tedious job that can easily be automated by a loop.

Computation of the sum in (1) by a while loop in Python, makes use of (i) a counter k that runs through odd numbers from 1 up to some given maximum power N, and (ii) a summation variable, say s, which accumulates the terms, one at a time. The purpose of each pass of the loop is to compute a new term and add it to s. Since the sign of each term alternates, we introduce a variable sign that changes between \( -1 \) and \( 1 \) in each pass of the loop.

The previous paragraph can be precisely expressed by this piece of Python code:

x = 1.2  # assign some value
N = 25   # maximum power in sum
k = 1
s = x
sign = 1.0
import math

while k < N:
    sign = - sign
    k = k + 2
    term = sign*x**k/math.factorial(k)
    s = s + term

print 'sin(%g) = %g (approximation with %d terms)' % (x, s, N)
The best way to understand such a program is to simulate it by hand. That is, we go through the statements, one by one, and write down on a piece of paper what the state of each variable is.

When the loop is first entered, k < N implies 1 < 25, which is True so we enter the loop block. There, we compute sign = -1.0, k = 3, term = -1.0*x**3/(3*2*1)) (note that sign is float so we always have float divided by int), and s = x - x**3/6, which equals the first two terms in the sum. Then we test the loop condition: 3 < 25 is True so we enter the loop block again. This time we obtain term = 1.0*x**5/math.factorial(5), which correctly implements the third term in the sum. At some point, k is updated to from 23 to 25 inside the loop and the loop condition then becomes 25 < 25, which is False, implying that the program jumps over the loop block and continues with the print statement (which has the same indentation as the while statement).