Trace tables
Following an algorithm line by line, and tracing to find a logic error.
Do this lesson in the simulatorA program that runs without an error can still be wrong. To find out what an algorithm really does, programmers trace it: follow it line by line, by hand, writing down the value of every variable each time it changes. A trace table is where you write it down. It is how you check an algorithm before trusting it, and how exams test whether you understand one.
A first trace
total = 0
for i in range(1, 5):
total = total + i * 2
print(total)
One column per variable, plus a column for output. A new row every time something changes:
| i | total | output |
|---|---|---|
| 0 | ||
| 1 | 2 | |
| 2 | 6 | |
| 3 | 12 | |
| 4 | 20 | |
| 20 |
Work through it yourself on paper first, then run the program to check:
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
total = 0
for i in range(1, 5):
total = total + i * 2
print(total)
Let the program trace itself
Adding a print inside the loop makes the program write its own trace table. This is exactly how programmers debug:
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
x = 1
y = 10
print("x", "y")
while x < y:
x = x * 2
y = y - 1
print(x, y)
print("finished with x =", x)
Before you run it, trace it on paper. When does the loop stop, and why? Then run it and compare row by row. The first row where your table and the program disagree is where your understanding and the algorithm part company.
The Debug button does the same job on screen: it stops at each line and shows the variables as they change.
Tracing to find a bug
This program should give the average of five distance readings, 40, 35, 30, 25 and 20, which is 30. It prints something else:
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
readings = [40, 35, 30, 25, 20]
for i in range(len(readings)):
total = 0
total = total + readings[i]
print("average:", total / len(readings))
It prints average: 4.0. No error message, so this is a logic error. A trace shows it straight away:
| i | readings[i] | total |
|---|---|---|
| 0 | 40 | 40 |
| 1 | 35 | 35 |
| 2 | 30 | 30 |
total never grows. It goes back to 0 every time round, because total = 0 is inside the loop. By the end, total holds only the last reading, 20, and 20 divided by 5 is 4.0.
Tracing a robot
The robot's position can go in a trace table too. Predict where this program leaves the robot, then run it:
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
side = 10
for i in range(3):
forward(60, distance=side)
right(60, distance=side)
side = side + 5
print("at", position())
| i | side before the drives | moved up | moved right |
|---|---|---|---|
| 0 | 10 | 10 | 10 |
| 1 | 15 | 15 | 15 |
| 2 | 20 | 20 | 20 |
So it should end 45 cm up and 45 cm right. The robot's own report will be close but not exact, because a real (or simulated) robot drifts a little.
Task: trace and fix
Fix the average program so it prints average: 30.0. Keep a trace: inside the loop, print a line i=<i> total=<total> each time round, so the program shows its own trace table.
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
readings = [40, 35, 30, 25, 20]
for i in range(len(readings)):
total = 0
total = total + readings[i]
print("average:", total / len(readings))
Challenges
- Trace
n = 27thenwhile n != 1: if n is even, n = n // 2, else n = 3 * n + 1for the first six rows. Then run it to see how long it really takes. - Write a trace table for the parking sensor's loop, with the distance, the gap and the beep count.
- Swap the two lines inside the
xandyloop above. Trace it again before running. Does it still stop?