A. Programming Concepts and Skills · Arrays and SubprogramsTraversing a List: Counting, Totals and Averages
A2.3 A1.6 A4.4About 70 minutes
Learning goals- I can visit every element of a list with an element loop or an index loop, and choose the right one.
- I can write an accumulator to find the total and the average of a list.
- I can count the elements that meet a condition, using a loop with an if inside it (a nested structure).
- I can trace a loop over a list in a trace table to predict its output.
Words to know
traverse
To visit every element of an array, usually from index 0 to the end, with a loop.
accumulator
A variable that builds up a result as a loop runs, e.g. a running total that starts at 0.
counter
An accumulator that goes up by 1 each time something happens.
nested structure
A control structure inside another one, such as an if inside a for loop.
trace table
A table with one column per variable and one row per step, used to follow a program by hand.
augmented assignment
A shortcut such as total += x, which means total = total + x.
A season of snow
A ski hill near Collingwood records how many centimetres of snow fall each day. At the end of the season the manager wants three numbers: the total snowfall, the average per day, and how many days had more than 10 cm. A list holds the data. A loop that visits every element, called traversing the list, finds the answers. These three algorithms (total, average, count) are on the curriculum's list of must-know array algorithms, and you will use them in almost every program from now on.
Remember from Programming Basics
for i in range(n): repeats n times with i = 0, 1, ..., n − 1. An if inside a loop runs on every pass. You also saw counters like count = count + 1. The new idea today is pointing the loop at a list.
Two ways to traverse
Python gives you two loop patterns for lists:
- Element loop:
for x in a: The loop variable takes each value in turn. It is short and clear. Use it when you only need to read the values. - Index loop:
for i in range(len(a)): The loop variable takes each index 0, 1, ..., len(a) − 1, and you reach the value with a[i]. Use it when you need to change elements, compare neighbours, or report where something is.
Because range(len(a)) stops at len(a) − 1, an index loop can never go out of bounds. That is why you should loop with range(len(a)) and not with a typed-in number.
The same three-element list traversed two ways. An element loop hands you the values; an index loop hands you the positions, and a[i] gets the value.The accumulator pattern: totals and averages
To add up a list:
- Before the loop, make an accumulator
total = 0. - Inside the loop, add each element:
total = total + x (or the shortcut total += x). - After the loop,
total holds the sum.
The average (mean) is the total divided by the number of elements, total / len(a).
snow = [12, 0, 5, 30, 2]
total = 0
for cm in snow:
total = total + cm
print("Total:", total)
print("Average:", total / len(snow))
Output: Total: 49 and Average: 9.8. 12 + 0 + 5 + 30 + 2 = 49, and 49 ÷ 5 = 9.8.
The accumulator total after each pass of the loop over snow. It starts at 0 and grows: 12, 12, 17, 47, 49. The last value (shaded differently) is the answer.Counting with a condition
To count the elements that pass a test, put an if inside the loop. A structure inside another structure is called a nested structure. Only when the test is true does the counter go up.
temps = [-14, -3, -22, -8, -17, 1, -11]
cold_days = 0
for t in temps:
if t < -10:
cold_days = cold_days + 1
print(cold_days, "days colder than -10")
Output: 4 days colder than -10. The four values that are less than −10 are −14, −22, −17 and −11. (−10 itself would not count, because the test is < and not <=.)
Changing every element
Suppose a teacher adds 5 bonus marks to every mark. An element loop cannot do it: the loop variable is just a copy of the value, so changing it does not change the list. Use an index loop and assign to a[i].
marks = [62, 75, 88]
for m in marks:
m = m + 5 # changes only the loop variable
print(marks)
for i in range(len(marks)):
marks[i] = marks[i] + 5 # changes the list
print(marks)
Output: [62, 75, 88] and then [67, 80, 93]. The first loop changed only the variable m each time; the second loop changed the elements themselves.
Tracing a loop
A trace table lets you run a program on paper. Make one column for each variable and add a row every time a variable changes. Tracing is the best way to find logic errors: the program runs, but gives a wrong answer.
Worked exampleExample 1: Trace table
Trace this program and predict the output.
a = [4, -2, 7, -5, 6]
pos_total = 0
neg_count = 0
for x in a:
if x > 0:
pos_total += x
else:
neg_count += 1
print(pos_total, neg_count)
- Make columns for x, pos_total and neg_count. Start: pos_total = 0, neg_count = 0.
- x = 4: positive, pos_total = 4.
- x = −2: not positive, neg_count = 1.
- x = 7: pos_total = 4 + 7 = 11.
- x = −5: neg_count = 2.
- x = 6: pos_total = 11 + 6 = 17.
- The loop ends and prints both accumulators. Notice that 0 would land in the
else and be counted as negative, which is a logic error if the list could contain 0. A safer test is elif x < 0:.
| Step | x | x > 0? | pos_total | neg_count |
|---|
| start | – | – | 0 | 0 |
| 1 | 4 | yes | 4 | 0 |
| 2 | -2 | no | 4 | 1 |
| 3 | 7 | yes | 11 | 1 |
| 4 | -5 | no | 11 | 2 |
| 5 | 6 | yes | 17 | 2 |
The trace table for Example 1. One row per pass of the loop.Worked exampleExample 2: Above-average marks
Write a program that finds the average of marks = [78, 91, 66, 85, 70] and then counts how many marks are above the average.
- You need the average before you can compare, so this takes two traversals: one to total, one to count.
- Loop 1: total = 78 + 91 + 66 + 85 + 70 = 390. Average = 390 ÷ 5 = 78.0.
- Loop 2: compare each mark with 78.0. 91 and 85 are greater. 78 is equal, not above, so it does not count.
- Code:
marks = [78, 91, 66, 85, 70]
total = 0
for m in marks:
total += m
average = total / len(marks)
above = 0
for m in marks:
if m > average:
above += 1
print("Average:", average)
print("Above average:", above)
Average: 78.0 and Above average: 2
Avoid dividing by zero
If the list could be empty, total / len(a) raises ZeroDivisionError. Check first: if len(a) > 0: before computing the average, and print a helpful message otherwise.
Just for funAverage facts: if you put one foot in a bucket of ice water and the other in a hot bath, on average you are perfectly comfortable. This is why programmers sometimes report the count, the highest and the lowest along with the average.
Hands-on activityYour own data, your own totals
You need: a computer with Python, and a week or more of real numbers from your life. Ideas: minutes of screen time each day (from your phone's settings), the daily high temperature from a weather app, or the price of each item on a grocery receipt.
- Type the numbers into a list.
- Write a program that prints the total, the average (rounded with
round(avg, 1)), and a count of how many values pass a test you choose (e.g. days over 120 minutes, days below 0 °C, items over $5). - Trace your program by hand for the first three elements in a trace table, then run it and compare.
- Change one value in the list and predict how each answer will change before running again.
What to look for: the accumulator starts at 0 before the loop, and the division happens after the loop.
Your turn
1Write an element loop that prints every name in names, one per line.
for name in names:
print(name)
2Write code that finds the total of costs = [4.25, 1.50, 3.00] with an accumulator.
total = 0
for c in costs:
total += c
print(total)
It prints 8.75.
3What is wrong with putting total = 0 inside the loop?
It would reset the total on every pass, so at the end total would hold only the last element.
4Predict the output:
a = [3, 8, 1, 9, 4]
c = 0
for x in a:
if x >= 4:
c += 1
print(c)
3 (8, 9 and 4 are at least 4).
5When must you use for i in range(len(a)) instead of for x in a?
When you need to change elements (a[i] = ...) or need the position of an element (e.g. to print "found at index 3").
6Write code that doubles every element of a in place.
for i in range(len(a)):
a[i] = a[i] * 2
7What error happens with print(sum_total / len(a)) when a is empty? How do you avoid it?
ZeroDivisionError. Check if len(a) > 0: first.
More practice: warm-up
1What does total += 5 mean?
2What should a counter start at?
3How many times does for x in [5, 6, 7, 8]: repeat?
4 times, once per element.
4In for i in range(len(a)): with len(a) == 6, what is the last value of i?
5What is the average of 4, 6 and 11?
(4 + 6 + 11) ÷ 3 = 21 ÷ 3 = 7.
More practice: on your own
1Predict the output:
a = [2, 5, 8]
s = 0
for i in range(len(a)):
s += a[i] * i
print(s)
21. 2 × 0 + 5 × 1 + 8 × 2 = 0 + 5 + 16.
2Write a program that counts how many words in words = ["snow", "ice", "toque", "sled", "mitts"] have more than 4 letters.
count = 0
for w in words:
if len(w) > 4:
count += 1
print(count)
It prints 2 (toque and mitts).
3This code should print the average, but prints 15.0 for [10, 20, 30]. Find the logic error.
a = [10, 20, 30]
total = 0
for x in a:
total += x
print(total / (len(a) + 1))
It divides by len(a) + 1 = 4 instead of 3, so it gets 60 ÷ 4 = 15.0. Fix: divide by len(a), giving 60 ÷ 3 = 20.0.
4Write code that prints the index of every negative temperature in temps.
for i in range(len(temps)):
if temps[i] < 0:
print(i)
An index loop is needed because the question asks for positions.
5Write code that finds both the total of the even numbers and the count of the odd numbers in a list a in one loop.
even_total = 0
odd_count = 0
for x in a:
if x % 2 == 0:
even_total += x
else:
odd_count += 1
For [1, 2, 3, 4] this gives even_total 6 and odd_count 2.
Challenge
1Write code that counts how many times the temperature rose from one day to the next in temps = [-5, -3, -8, -1, 2, 2]. Why does the loop use range(len(temps) - 1)?
rises = 0
for i in range(len(temps) - 1):
if temps[i + 1] > temps[i]:
rises += 1
print(rises)
It prints 3 (−5→−3, −8→−1, −1→2; 2→2 is not a rise). The loop compares temps[i] with temps[i + 1], so i must stop at len − 2, or temps[i + 1] would be out of bounds.
2Write a program that reads marks (one per line) until the user enters −1, stores them in a list, and prints the number of marks and the average. Make sure entering −1 right away does not crash.
marks = []
m = int(input())
while m != -1:
marks.append(m)
m = int(input())
print(len(marks), "marks")
if len(marks) > 0:
total = 0
for x in marks:
total += x
print("Average:", total / len(marks))
else:
print("No marks entered")
With inputs 80, 90, −1 it prints 2 marks and Average: 85.0. With −1 alone it prints 0 marks and No marks entered (the −1 is a sentinel, not a mark).
3The range of a data set is highest − lowest. Without using max() or min(), explain why you cannot find the range of a list with one accumulator that starts at 0, and say what you would track instead.
A sum-style accumulator only adds; it cannot remember the largest or smallest value. You need two trackers: highest and lowest, both starting at the first element, updated whenever a bigger or smaller element appears. That is the max/min algorithm in the next lesson, Highest, Lowest and Linear Search.
Check your understandingQuiz
1. Where should total = 0 go when adding up a list?
2. What does this print? a = [5, 1, 4], t = 0, for x in a: t += x, print(t / len(a))
3. Which loop can change the elements of list a?
4. A loop counts days with if t < -10:. Which day is not counted?
5. What is a nested structure?
6. For a = [8, 3, 6], which index loop visits every element without going out of bounds?
For parents and teachersBig idea: Expectation A2.3 names these array algorithms: counting elements, calculating a total, finding the highest or lowest value, and linear search. This lesson covers counting, totals and averages. A4.4 (tracing) is practised with trace tables.
How to help: Ask your teen to trace a short loop out loud while you write the trace table, then swap roles. Real data from home (receipts, temperatures, screen time) makes the activity meaningful.
Common difficulties:
- Starting the accumulator inside the loop, or forgetting to start it at all (NameError).
- Trying to change elements with an element loop (
for x in a: x = ...). - Off-by-one when comparing neighbours: use
range(len(a) - 1) when the body uses a[i + 1]. - Dividing by zero for an empty list.
- Using
< when the question means "at most" or "at least" (<=, >=). Read the wording carefully.