while Loop Programs
A while loop is for the jobs where nobody knows how many rounds it will take — chopping the digits off a number, or reading values until the user types the one that means stop. Nothing counts for you here, so every one of these programs has a line whose only job is to move the loop towards its end.
1Program 1 — add up the digits of a number
Ask for a number and print the sum of its digits: 4271 gives 4 + 2 + 7 + 1 = 14.
- a whole number,
num
- take the last digit with
num % 10 - add it to a total
- chop that digit off with
num // 10 - repeat while there is anything left
- the total of the digits
# add up the digits of a number: 4271 -> 4 + 2 + 7 + 1
num = int(input('Enter a number: '))
number_typed = num
total = 0
while num > 0:
digit = num % 10
total = total + digit
num = num // 10
print('The digits of', number_typed, 'add up to', total)Enter a number: 4271 The digits of 4271 add up to 14
number_typed = numA spare copy, made before the loop touches anything. The loop destroys num on its way to zero, so without this copy there is nothing left to print at the end.
while num > 0:The loop runs while there are digits left. Once the last one has been chopped off, num is 0 and the condition is false.
digit = num % 10The remainder after dividing by 10 is the last digit: 4271 % 10 is 1. This reads the digit without removing it.
num = num // 10Floor division by 10 throws the last digit away: 4271 // 10 is 427. This is the line that moves the loop towards its end — delete it and the program never stops.
//, not /. 4271 / 10 is 427.1, a float — and a float never reaches exactly 0 by this route, so the loop would run on nonsense values. Digit work is always % and //.2Program 2 — how many digits does it have?
Ask for a number and print how many digits it has.
# how many digits does a number have?
num = int(input('Enter a number: '))
number_typed = num
count = 0
while num > 0:
count = count + 1
num = num // 10
print(number_typed, 'has', count, 'digits')Enter a number: 4271 4271 has 4 digits
The same skeleton as program 1, with the counter going up by 1 instead of by the digit — so the digit itself is never even read. Recognising that two problems share a shape is most of what practice is for.
Enter a number: 0 0 has 0 digits
0 > 0 is false, so the loop never runs a single round and the count stays at 0. We ran it — that is a real transcript above. The honest fix is an if num == 0: before the loop that answers 1, and the same hole is in the digit-sum and reverse programs too.3Program 3 — reverse a number
Ask for a number and print it with its digits in the opposite order: 4271 becomes 1724.
# reverse a number: 4271 -> 1724
num = int(input('Enter a number: '))
number_typed = num
backwards = 0
while num > 0:
digit = num % 10
backwards = backwards * 10 + digit
num = num // 10
print(number_typed, 'reversed is', backwards)Enter a number: 4271 4271 reversed is 1724
One line does the work: backwards = backwards * 10 + digit. Multiplying by 10 pushes everything collected so far one place to the left, which leaves an empty units place for the new digit. Round by round with 4271, that is 1, then 17, then 172, then 1724.
4Program 4 — is the number a palindrome?
A palindrome number reads the same backwards: 4554, 121, 7. Ask for a number and say whether it is one.
# a palindrome number reads the same backwards: 121, 4554, 7
num = int(input('Enter a number: '))
number_typed = num
backwards = 0
while num > 0:
digit = num % 10
backwards = backwards * 10 + digit
num = num // 10
if backwards == number_typed:
print(number_typed, 'is a palindrome')
else:
print(number_typed, 'is not a palindrome')Enter a number: 4554 4554 is a palindrome
Enter a number: 123 123 is not a palindrome
Program 3 with four lines added, and this is where number_typed stops being a convenience and becomes necessary: the comparison needs the original number, and num is zero by the time the loop ends. Comparing backwards == num would ask whether the reversal equals zero.
if is outside the loop. At the margin, after the loop, because the question can only be answered once the whole number has been reversed. Inside the loop it would be asked four times, of a half-built answer.5Program 5 — keep asking until the user says stop
Read marks one at a time and total them. The user types 0 when there are no more. Print how many marks were entered and their total.
# keep asking for marks until 0 is typed, then report the total
total = 0
count = 0
mark = int(input('Enter a mark (0 to stop): '))
while mark != 0:
total = total + mark
count = count + 1
mark = int(input('Enter a mark (0 to stop): '))
print('You entered', count, 'marks')
print('Their total is', total)Enter a mark (0 to stop): 45 Enter a mark (0 to stop): 78 Enter a mark (0 to stop): 62 Enter a mark (0 to stop): 0 You entered 3 marks Their total is 185
This is the program a for loop cannot write. Nobody — not the programmer, not the user — knows how many marks there will be until the 0 arrives. A value that means “that is the lot” like this one is called a sentinel, and it is never added to the total, because the loop ends before the body is reached.
input() line is written twice, and it has to be. The first one gives the condition something to test on the very first round; the one at the end of the body is what moves the loop on. Delete the first and the name mark does not exist yet, so the header raises NameError. Delete the second and the same mark is added for ever.6Program 6 — a countdown
Ask for a number and count down from it to 1, then print Lift off!.
# a countdown, which a for loop would do just as well
n = int(input('Count down from: '))
while n > 0:
print(n)
n = n - 1
print('Lift off!')Count down from: 5 5 4 3 2 1 Lift off!
Here the count is known in advance, so for i in range(n, 0, -1): would do the same job in one line fewer and could not be made to run for ever. Both are correct; for is the better answer to this particular question. Being able to say why is worth more than either program.
7The two endless loops that pass the checklist
The rule at the top of this page — set up, test, change — catches the commonest endless loop, the one with no update line at all. It does not catch these two, and that is exactly why they are worth meeting: both have all three parts present and correct, and neither ever finishes.
The first one changes the loop variable, faithfully, in the wrong direction:
# count down to zero -- except the counter goes the wrong way
i = 5
while i > 0:
print(i)
i = i + 1
print('Done')5 6 7 8 9 10 11 12
Every part is there: i is set up before the loop, tested in the header, and changed in the body. But the test asks is i still above zero? and the change makes i bigger. The condition was true at 5 and every round makes it more true. We ran it with a round cap rather than let it go: after eight rounds i was 13 and i > 0 was still True. One character fixes it — i = i - 1 — and then it prints 5, 4, 3, 2, 1 and Done.
i to get smaller, the body had better be making it smaller. Reading them one at a time is how this bug survives being looked at.The second is nastier, because the fault is in a line that was added to help. This program accepts a capital or a small letter — and then waits for a value that can now never arrive:
# keep going until the user types q -- which this program can never notice
letter = input('Type a letter (q to quit): ')
letter = letter.upper()
while letter != 'q':
print('You typed', letter)
letter = input('Type a letter (q to quit): ')
letter = letter.upper()
print('Goodbye')Type a letter (q to quit): q You typed Q Type a letter (q to quit): q You typed Q Type a letter (q to quit): q You typed Q Type a letter (q to quit): q You typed Q
letter.upper() turns every answer into a capital, so the q the user types is 'Q' by the time the condition sees it — and 'Q' != 'q' is True. The loop is waiting for a value the program has made impossible. Change the condition to while letter != 'Q': and it quits on the first q, capital or not, which is what the upper() was for in the first place.
'10' with 10: the loop is not wrong about what it wants, it is wrong about what it is being handed.- 1Is there a line in the body that changes what the condition tests? No line at all is the commonest fault.
- 2Does that change move towards the condition being false? Read the header and the update line as a pair.
- 3Can the value the condition waits for actually be produced? Follow it from where it is typed to where it is tested.
8Recap
A variable made before the loop, tested in the header, and moved in the body. If you cannot point at all three, the loop is endless.
% 10 reads the last digit, // 10 throws it away. Plain / gives a float and the loop stops behaving.
These loops eat the number they are given. Anything you need to print or compare at the end must be copied before the loop starts.
i = i + 1 under while i > 0 is an endless loop with all three parts present. Read the header and the update line together, as a pair.
A sentinel that gets upper-cased, rounded or cast between the keyboard and the test never matches, and the loop waits for ever.
One input() before the loop to get started, one at the foot of the body to move on. The sentinel value itself is never processed.
- 1
Print the product of a number's digits instead of their sum.
Hint · The same loop with a collector that starts at 1 and multiplies.
- 2
Count how many even digits a number has (4271 has two: 4 and 2).
Hint · Read the digit as usual, then ask
digit % 2 == 0before counting it. - 3
Keep asking for a password until the user types
lambda, then printAccess granted.Hint · The sentinel shape, with text instead of numbers — so no
int()anywhere. - 4
Ask for a number and print how many times it can be halved before it drops below 1.
Hint · The changing line is
num = num / 2, and the counter goes up beside it. - 5
Check whether a number is an Armstrong number: 153 = 1³ + 5³ + 3³.
Hint · Digit-sum, with
digit * digit * digitadded instead ofdigit. Count the digits first if you want it to work beyond three.
A digit-sum loop has the line num = num // 10 deleted. What happens?
i = 5, then while i > 0: print(i) and i = i + 1. The loop has a set-up, a test and a change. What does it do?
Why does the palindrome program compare backwards with number_typed rather than with num?
In the sentinel program, why is input() written both before the loop and at the end of the body?