Loops and conditions

We’ll now look at programming constructs that allow lines of code to be executed multiple times. But first, we’ll need to learn a bit more about expressions and operators.

Conditional operators: >, <, >=, <=, ==, !=

Each value in Python has a type: int, float, string, boolean, etc. A boolean can have either the value True or the value False. In Python, certain operators compute values that are True or False. An expression that computes a True or False value is called a boolean expression. Let’s look at an example:

x < 4 is a boolean expression. In Python, the less-than symbol is called a conditional operator, and like other operators, it actually computes a value. In fact, it computes a boolean value of True or False. In this example, since x currently has the value 5, the expression x < 4 evaluates to the value False.

You can also store the results of a conditional operator in a variable:

In mathematics, the equation c = 5 > 6 makes no sense. In Python, the line of code c = 5 > 6 is perfectly fine: compute the expression 5 > 6, which gives False, and then assign that False value into the variable c.

There are several conditional operators:

Common programming error: using = when you meant to use ==

Look at the next lines of code:

The line of code print( x = 4 ) looks like it checks so see whether x = 4, but it does not. Recall: a single = sign is the assignment operator. You cannot put it inside a print statement, and so Python gives you an “invalid syntax” error. (The browser version of Python might not give you the error, but it’s still wrong.)

You almost certainly wanted the version with ==. The == operator computes a True or False value based on equality of its operands, so that x == 4 computes to the value False.

Conditional operators work on floats, strings, and boolean values, too

Here are some examples of conditionals working on other data types:

Common programming error: using == to compare floats

Floating-point numbers have limited precision. There are typically 64 bits assigned to each float. Recall that there are 264 different patterns of 64 bits. Since each floating-point number needs its own unique pattern of bits, Python can only represent 264 possible different floats. There are infinitely many real numbers, and so floats must have limited precision.

In practice, this means that over time, small roundoff errors can build up in computations involving floats.

In this program, we see that Python only has an approximate value for π. The numerical computation of the sin function is also approximate. We get an answer of 1.22464679915e-16, but you probably remember from math class that sin(π) is equal to 0. If you compare 1.22464679915e-16 to 0, Python gives the answer False. Yes, 1.22464679915e-16 is close to 0, but it is not equal to 0.

One way to more safely compare floating-point numbers for equality is to check whether the absolute value of the difference of the numbers is less than some small constant (often called “epsilon”), which you will have to set.

Logical operators and, or, and not

The conditional operators ==, <, >, etc, can be used to compare numbers, strings, or other types, and return a boolean. In the expression 5 < 4, the less-than operator takes two integers and returns a boolean.

The operators and and or take two boolean values and evaluate to a boolean value:

The not operator just takes a single boolean value and evaluates to the complement, i.e., the opposite. The complement of False is True, and the complement of True is False.

Exercise: even in a range

Objective: Write a boolean expression to test if a variable satisfies several conditions.

Write a single line of code that tests if the value of the variable x is even and has a value in the range 24 and 32 (inclusive), and prints out True if so, and False otherwise. Test your code with the values 0, 3, 24, 25, 32, 33, and 34.

Short-circuiting of and and or

Notice that if the first operand of and evaluates to False, we’re done: we know that the result of and must be False, regardless of the second operand. We don’t even have to look at the second operand. Python short-circuits if it sees that the first operand of an and is False; it doesn’t evaluate the second operand at all! This saves Python some computation, but the real advantage is that it allows you to avoid a potentially disastrous situation. Here’s an example:

If not for the short-circuiting, Python would try to compute 3/x. But x is 0, and so Python would complain and give a “integer division or modulo by zero” error, terminating the program.

Likewise, if the first operand of or evaluates to True, we know that the result of or must be True, regardless of the second operand. Again, Python short-circuits and does not evaluate the second operand whenever the first operand of an or is True.

Loops

Let’s say that you would like to print the phrase “All work and no play makes Jack a dull boy.” infinitely many times. Using the techniques we’ve seen so far, this would require infinitely many lines of code:

A loop structure directs Python to move the program counter to a previous line of code under certain conditions, and loops can cause the same line of code to be executed many times:

We’ve already seen the program counter: the bookmark that Python uses to keep track of what line of code is currently being executed. We can use a while-loop to cause the program counter to loop back to an earlier line in the program, causing certain lines of code to be executed over and over again.

A while-loop has two parts: a header and a body. The header looks like this:

while condition:

The condition is an expression that evaluates to a boolean value: either True or False. Notice that while is written in lowercase, and there is a colon after the condition.

The body of a while-loop is made up of the lines of code that we want to be executed multiple times. Just like a function, each line of code in the body is indented, using the tab key, to indicate that it is part of the body of the while-loop.

Here’s what happens when Python reaches a line of code containing a header for a while-loop:

  1. Python checks to see whether the condition evaluates to the value True.
  2. If the condition evaluates to True, then Python executes the body of the while-loop. When Python gets to the end of the body (it runs out of indented lines), it goes back to the header and repeats step 1.
  3. Once the condition evaluates to False, Python skips over the body of the while-loop, and goes to the next line with the same level of indentation as while.

Notice that Python evaluates the condition only when it enters the loop and when it goes back to the header after having reached the bottom of the loop body. If something happens in the middle of the loop body that causes the condition to evaluate to False, the loop doesn’t terminate until Python gets to the end of the body and goes back up to evaluate the condition in the header.

In our example, Python checks to see whether the condition evaluates to True. It sure is; in fact, it always evaluates to True. So Python executes the lines of code in the body until the body is finished. Then Python goes back up to the header. Is the condition True? Yep. So Python executes the body. Then to the header. Condition True? Yes. Keep going.

At this point I’m sure you see a pattern in the example. That condition is always going to be True. It says True right there. This loop is called an infinite loop, since it will run forever until you force a quit somehow. The only way to stop the program is to “kill” it, stopping it using some extreme external means. REDRUM!

Programs with infinite loops are not always bad. A robot might be intended to act forever, and the structure of the code might be an infinite loop considering and taking actions. However, unintentional infinite loops are a common programming error, and can have drastic unintended consequences, like causing the user’s computer to become unresponsive while all available computation power is used running the loop.

In the browser, infinite loops cause the page to crash. There is no button to run the example code with an infinite loop above, and you should avoid infinite loops particularly in code you write in these tutorials within the browser.

Counting with a while-loop

We call each time through the while-loop an iteration. So, on the first iteration, n initially has the value 1, is printed, and then is incremented from 1 to 2. Increment means to increase the value by 1. On the second iteration, n initially has the value 2, is printed, and then is incremented from 2 to 3. Eventually, n gets the value 10, is printed, and is incremented to 11. When we get back to the condition in the while-loop header, the condition n <= 10 evaluates to False, and we drop out of the while-loop.

Let’s write a simple program that counts from 1 to 5.

Every line of code is different in this example! How can we write a loop that will repeat lines of code, and count from 1 to 5? The trick is that variables can change the behavior of a line of code. First, re-write the code this longer and more awkward way:

Notice that the lines grouped into pairs are repeated five times, and look the same every time. We can use a loop!

When should the loop stop? i has the value 6 after the last step, so we should repeat the body as long as i is less than 6. In python, the code i < 6 is a computable expression. The < sign is called a conditional operator, and it takes two values and produces a boolean True or False value. For example, 3 < 4 evaluates to True, and 4 < 3 evaluates to False. So i < 6 evaluates to True if and only if the value in i is less than 6.

One major use of conditional operators is to provide termination conditions for a while-loop. while True will run forever. We need to choose a condition that might eventually become False. One choice is a boolean expression involving a variable whose value changes.

Exercise: count while

Objective: Write a while loop that terminates after counting up to a certain value.

Write a program to count to five and print the output as the counter increases.

This program is not shorter than the first program for counting to 5, but what if you wanted to count to 100?

Exercise: Count down

Objective: Write a while loop that changes a variable and stops when that variable reaches a particular value.

Write a program that counts down from 100 to 0 backwards by twos. If you need to, write out all of the print statements by hand, look at what’s changing, and introduce a variable to allow that change using repeated lines of code. Then wrap into a while loop.

Exercise: series

Objective: Use multiple variables in a while loop to change loop body behavior on each iteration.

Write a program using a loop that prints first 100 numbers in the series of numbers: 1, 2, 4, 7, 11, 16, 22. Hint: The difference between the two consecutive numbers increases by one for each new number. You may need a variable to keep track of the current rate of increase.

Here is a solution. No peeking until you have solved it yourself. (Exercise contributed by Vasanta Lakshmi Kommineni.)

while-loop example: dancing robots

Next, we will use a simple library to simulate controlling Roomba vacuum-cleaner mobile robots. Here is a short example:

Exercise: square dance

Objective: Build complex behavior from a repeating sequence of simple behaviors.

Add only forwards and left function calls to cause the robot to move in a square:

Exercise: 5 squares

Objective: write a while loop to cause repeated behavior.

Now write a program using a single while-loop that causes the robot to drive 4 squares in a row.

Exercise: square function

Objective: Use a function to isolate a sequence of instructions, encapsulating a complex behavior. This is an example of abstraction.

Write a function single_square() that causes the robot to do a single square. Also write a while loop that calls single_square() 5 times, to cause the robot to drive in 5 squares.

if-statements

The body of a while-loop may be executed any number of times, zero or more. What if you wanted to check whether a condition was True, and if so, execute some lines of code exactly once? We do so with an if-statement:

Nesting of while-loops and if-statements

Both while-loops and if-statements allow you to control how Python flows from one line of code to another in your program. while and if are therefore called flow-of-control constructs. You can also put one while-loop or if-statement inside another.

It is very common to see an if-statement inside a while-loop. For example, here is a program to print all of the integer factors of a number. The approach is to generate all positive integers between 1 and the number. Each time you generate a new integer, test to see whether it is a factor. If it is, then print it.

The if-statement header is indented once, because it is part of the body of the while-loop. The line starting with print str(possible_factor) is indented once from the if, since it is part of the if-statement body. The line starting with possible_factor is indented once from the while-loop’s header, since it is part of the while-loop’s body, but not part of the if-statement’s body. The final print is not indented because it is not part of the while-loop’s body.

We call the pattern that this program follows generate and test. We would like to find some answers to a problem. We have a way of testing whether a hypothesized answer is actually a solution. So we generate all reasonably possible answers, and test to see whether each is an answer. Such problems will often have a loop to generate the possible answers, and some series of tests (with if-statements) in the body of the loop to check whether a possible answer is correct.

When you see a new problem, it’s worth figuring out if the new problem could be solved by a generate-and-test approach. If so, you now have an idea about how you might implement a solution in code.

Reading loops

When reading code containing a while-loop, try doing a couple of things:

  1. First, think like a computer. Walk through the code one line at a time, carefully. Don’t assume anything. Be one with the computer. Say: “while-loop header.” Check the condition. Does it evaluate to True? It does? Then execute the body. While-loop header. Check the condition. Does it evaluate to True? Write down values of the variables as they change.
  2. After you have read some code this way, look for a pattern.

A classic mistake for beginning programmers is to look for the pattern too soon. Make sure you really step through the code carefully before you make an assumption about the pattern.

As a rule of thumb, check that your while-loop works when the body executes zero times, exactly one time, and exactly three times. There’s a little programming ditty that I heard decades ago and still sticks in my mind:

Zero, one, three—that’s good enough for me.

Exercise: walking the loop

Objective. Trace through the execution of a while-loop by hand.

Here’s the code to find integer factors again:

Step through the code mentally, recording into the text box below the value of the program counter, the values of variables, the values of interesting expressions, and output to the screen; some values have been entered to get you started.

Exercise: optimize factors

Objective. Modify existing code to implement a more efficient algorithm.

The code for computing factors is inefficient. It loops from 1 to the number, but I claim it should only loop from 1 to the square root of the number, since factors come in pairs. For example, if 2 is a factor of 42, then so is 21. Re-write the following code so that it iterates fewer times through the loop, but still prints out all of the factors. Don’t forget to import the sqrt function from the math module.

else-statements

Frequently, you want to execute one set of lines of code if a condition holds, but execute a different set of lines if that condition does not hold. In this case, you can use an else-statement:

An else-statement must follow an if-statement at the same level of indentation, and it has no condition after it, since it is executed if the condition of the matching if statement is False. Note that there’s a colon after else.

Exercise: the lion and the unicorn

Objective: use a generate-and-test pattern to write a loop with changing behavior.

Write a program that counts from 1 to 20 and along with each number prints “Lion” if the number is odd and prints “Unicorn” if the number is even. The first four lines of output should be:

1 Lion
2 Unicorn
3 Lion
4 Unicorn

Here is a solution. No peeking until you have solved the problem yourself. (Exercise contributed by Vasanta Lakshmi Kommineni.)

elif-statements

Sometimes you want to check whether some condition is True only if some series of previous conditions were all False. You can use elif, which is short for “else if.” Like in elif.py:

We often call a statement sequence of if-elif-elif-…-elif-else an if-ladder.