LambdaLabTM
Computer Science · Class 11 · Practice Programs
PracticeCount unknown⏱️ 16 min read

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.

The lesson these programs practiseThe while Loop
Key Takeaway
Three things, or it never ends. Something must be set up before the loop, tested in the header, and changed inside the body. Miss the third and the condition stays true for ever. Every program on this page is worth checking against those three before you run it.

1Program 1 — add up the digits of a number

📋 The problem

Ask for a number and print the sum of its digits: 4271 gives 4 + 2 + 7 + 1 = 14.

Input
what we ask the user for
  • a whole number, num
Process
what we work out
  • 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
Output
what we show
  • the total of the digits
sum_digits.py
# 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)
Output
Enter a number: 4271
The digits of 4271 add up to 14
number_typed = num

A 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 % 10

The remainder after dividing by 10 is the last digit: 4271 % 10 is 1. This reads the digit without removing it.

num = num // 10

Floor 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.

Watch Out
//, 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?

📋 The problem

Ask for a number and print how many digits it has.

count_digits.py
# 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')
Output
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.

count_digits.py — and the number it gets wrong
Output
Enter a number: 0
0 has 0 digits
Watch Out
Zero has no digits, says this program. 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

📋 The problem

Ask for a number and print it with its digits in the opposite order: 4271 becomes 1724.

reverse_number.py
# 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)
Output
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.

Tip
This is the string reversal from the last page, in numbers. There the new letter was joined on the front of a string; here the old number is shoved left to make room. Same idea, and the same reason the order of the two parts cannot be swapped.

4Program 4 — is the number a palindrome?

📋 The problem

A palindrome number reads the same backwards: 4554, 121, 7. Ask for a number and say whether it is one.

palindrome_number.py
# 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')
Output
Enter a number: 4554
4554 is a palindrome
palindrome_number.py — a number that is not one
Output
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.

Key Takeaway
The 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.
palindrome_number.py

5Program 5 — keep asking until the user says stop

📋 The problem

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.

sentinel_total.py
# 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)
Output
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.

Watch Out
The 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

📋 The problem

Ask for a number and count down from it to 1, then print Lift off!.

countdown.py
# 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!')
Output
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:

runaway.py
# count down to zero -- except the counter goes the wrong way

i = 5

while i > 0:
    print(i)
    i = i + 1

print('Done')
runaway.py — the first eight lines. It does not stop after them.
Output
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.

Key Takeaway
“It changes” is not the question. “Does it change towards the condition becoming false?” is. Read the header and the update line together, as a pair: if the header waits for 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:

quit_bug.py
# 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')
quit_bug.py — typing q, four times over. Goodbye never arrives.
Output
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.

Key Takeaway
Ask whether the value the condition is waiting for can actually turn up. A sentinel that is filtered, rounded or converted somewhere between the keyboard and the test is a sentinel that never matches. This is the same mistake as comparing '10' with 10: the loop is not wrong about what it wants, it is wrong about what it is being handed.
Watch Out
Do not run either of these here. Both outputs above were produced with a limit on the number of rounds, because a real endless loop would lock this page — the Python on it shares your browser tab. In IDLE, Ctrl + C stops one.
🔁 Three questions before you run any while loop
  1. 1Is there a line in the body that changes what the condition tests? No line at all is the commonest fault.
  2. 2Does that change move towards the condition being false? Read the header and the update line as a pair.
  3. 3Can the value the condition waits for actually be produced? Follow it from where it is typed to where it is tested.

8Recap

Set up · test · change

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.

Digit work is % and //

% 10 reads the last digit, // 10 throws it away. Plain / gives a float and the loop stops behaving.

Keep a copy of the input

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.

Changing is not enough — it must change the right way

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.

The value the condition waits for has to be reachable

A sentinel that gets upper-cased, rounded or cast between the keyboard and the test never matches, and the loop waits for ever.

Sentinel loops read twice

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.

✍️ Now write these yourself
  1. 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. 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 == 0 before counting it.

  3. 3

    Keep asking for a password until the user types lambda, then print Access granted.

    Hint · The sentinel shape, with text instead of numbers — so no int() anywhere.

  4. 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. 5

    Check whether a number is an Armstrong number: 153 = 1³ + 5³ + 3³.

    Hint · Digit-sum, with digit * digit * digit added instead of digit. Count the digits first if you want it to work beyond three.

Quick Check

A digit-sum loop has the line num = num // 10 deleted. What happens?

Quick Check

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?

Quick Check

Why does the palindrome program compare backwards with number_typed rather than with num?

Quick Check

In the sentinel program, why is input() written both before the loop and at the end of the body?