Grade by Grade
A. Programming Concepts and Skills · Programming Basics: Data, Decisions and Loops

Nested Structures: Counting, Totals, Highest and Lowest

A2.3 A2.2 A2.1About 75 minutes
Learning goals
  • I can put selection inside a loop to count or total only some values.
  • I can find the highest and lowest of a set of input values.
  • I can calculate an average safely.
  • I can write and trace nested loops, and work out how many times the inner body runs.
  • I can combine loops and selection to solve a multi-step problem.

Words to know

nested structure
A control structure placed inside another, such as an if inside a loop or a loop inside a loop.
counter
A variable that starts at 0 and goes up by 1 each time something happens.
running maximum / minimum
A variable holding the largest (or smallest) value seen so far, updated as each new value arrives.
outer loop / inner loop
In nested loops, the inner loop is inside the outer loop's body and runs completely for each iteration of the outer loop.
algorithm
A precise, step-by-step method for solving a problem that always finishes.

Putting the pieces together

You now have all three control structures: sequence, selection and repetition. Real algorithms nest them. Expectation A2.3 names four classic nested algorithms: counting items, calculating a total, finding the highest or lowest value, and linear search. In this unit you will apply the first three to values that arrive one at a time (from the keyboard or a range). In the unit Arrays and Subprograms you will run the same algorithms, plus linear search, over data stored in a list.

Counting and totalling with conditions

An if inside a loop lets you count or add only some of the values.

  • Count: count = 0 before the loop; inside, if condition: count += 1.
  • Conditional total: total = 0 before the loop; inside, if condition: total += value.

Accumulators are always initialized before the loop and reported after it. Putting count = 0 inside the loop resets it every time, a very common bug.

days_below = 0
total = 0
for day in range(1, 6):
temp = int(input(f"Day {day} high (°C): "))
total += temp
if temp < 0:
days_below += 1
print(f"Average high: {total / 5:.1f} °C")
print(f"Days below zero: {days_below}")

A loop with an if nested inside: every temperature is totalled, but only the ones below zero are counted. Output, line by line: Day 1 high (°C): -4 | Day 2 high (°C): 3 | Day 3 high (°C): -11 | Day 4 high (°C): 8 | Day 5 high (°C): 2 | Average high: -0.4 °C | Days below zero: 2 (the typed input is shown after each prompt)

Highest and lowest

To find the largest value, keep a record holder and challenge it with every new value:

  1. Read the first value and make it both the highest and the lowest so far.
  2. For each remaining value: if it is greater than highest, it becomes the new highest; if it is less than lowest, it becomes the new lowest.
  3. After the loop, the records hold the answers.

Why start with the first value? If you start highest = 0 and every temperature is below zero, the answer would wrongly be 0. Starting with real data always works.

temphighestlowest-4-4-433-4-113-1188-1128-11shaded box = the value changed at this step
Finding the highest and lowest of five temperatures in one pass. Both start as the first value (−4); each new value replaces the record only if it beats it. Final: highest 8, lowest −11.
n = int(input("How many temperatures? "))
temp = int(input("Temperature: "))
highest = temp
lowest = temp
for i in range(n - 1):
temp = int(input("Temperature: "))
if temp > highest:
highest = temp
if temp < lowest:
lowest = temp
print("Highest:", highest, "Lowest:", lowest)

The first value is read before the loop, so the loop runs n - 1 more times. Two separate ifs are used; elif would also work, because one new value cannot beat both records at once. Output, line by line: How many temperatures? 5 | Temperature: -4 | Temperature: 3 | Temperature: -11 | Temperature: 8 | Temperature: 2 | Highest: 8 Lowest: -11 (the typed input is shown after each prompt)

Averages need a guard

An average is total / count. If no values were entered (for example, the user typed the sentinel first), count is 0 and the division crashes. Guard it: if count > 0: print the average, else: print a message.

Loops inside loops

A loop can also go inside another loop. The inner loop runs completely, from start to finish, for every iteration of the outer loop. If the outer loop runs 3 times and the inner loop 4 times, the inner body runs 3 × 4 = 12 times.

Nested loops are natural for anything with rows and columns: a times table grid, a seating chart, the pixels of an image, or every pair of players in a tournament.

rowcol = 1, 2, 3, 4 (inner loop)row = 1(1, 1)step 1(1, 2)step 2(1, 3)step 3(1, 4)step 4row = 2(2, 1)step 5(2, 2)step 6(2, 3)step 7(2, 4)step 8row = 3(3, 1)step 9(3, 2)step 10(3, 3)step 11(3, 4)step 12outer loop: 3 rows × inner loop: 4 columns = 12 iterations of the inner body
In a nested loop, the inner loop runs completely for each value of the outer loop. The steps are numbered in the order they happen: all of row 1, then all of row 2, then row 3.
for row in range(1, 4):
for col in range(1, 5):
print(f"{row * col:>3}", end="")
print()

A 3 × 4 multiplication grid. The inner loop prints one row of numbers on the same line; the print() after it (indented once, inside the outer loop only) ends the row. Output, line by line: 1 2 3 4 | 2 4 6 8 | 3 6 9 12

Worked example

Example 1: A triangle pattern

Write nested loops that print this pattern for n = 4: a row of 1 #, then 2, then 3, then 4.

  1. The outer loop chooses the row: for row in range(1, n + 1):.
  2. Row number row needs row symbols, so the inner loop is for k in range(row):. The inner range depends on the outer variable.
  3. Inside the inner loop: print("#", end=""). After the inner loop (still inside the outer loop): print().
  4. Count: the inner body runs 1 + 2 + 3 + 4 = 10 times in all, not 4 × 4, because the inner range changes.

See the program below. Output: #, ##, ###, ####.

n = 4
for row in range(1, n + 1):
for k in range(row):
print("#", end="")
print()

Example 1. Changing range(row) to range(n - row + 1) would turn the triangle upside down. Output, line by line: # | ## | ### | ####

Worked example

Example 2: Several students, several marks

A teacher enters 3 marks for each of 2 students. For each student, print their average. At the end, print how many students averaged 70 or more.

  1. Outer loop: one iteration per student. Inner loop: one iteration per mark.
  2. The student's total must be reset to 0 at the start of each outer iteration (inside the outer loop, before the inner loop), because each student has their own total.
  3. The count of strong students is set to 0 before both loops, because it counts across all students.
  4. After the inner loop, compute the average, print it, and use an if to update the count.
  5. Trace with marks 80, 65, 77 (average 74) and 60, 72, 70 (average 67.3): one student is 70 or above.

See the program below: it prints each average and then Students averaging 70+: 1.

strong = 0
for student in range(1, 3):
total = 0
for test in range(1, 4):
mark = int(input(f"Student {student}, test {test}: "))
total += mark
average = total / 3
print(f"Student {student} average: {average:.1f}")
if average >= 70:
strong += 1
print("Students averaging 70+:", strong)

Three levels of nesting: an if and an inner loop inside an outer loop. Notice exactly where each accumulator is reset. Output, line by line: Student 1, test 1: 80 | Student 1, test 2: 65 | Student 1, test 3: 77 | Student 1 average: 74.0 | Student 2, test 1: 60 | Student 2, test 2: 72 | Student 2, test 3: 70 | Student 2 average: 67.3 | Students averaging 70+: 1 (the typed input is shown after each prompt)

Just for fun

Why was the inner loop so tired?

Every time the outer loop took one step, the inner loop had to run all the way around.

Hands-on activity

A week of weather

You need: a weather app, the newspaper, or a family member's memory of this week's weather; paper and Python.

  1. Record the daily high temperature for the last 7 days (whole degrees). If you cannot get real data, use: 4, −2, −7, 1, 6, −3, 0.
  2. Before coding, work out by hand: the total, the average to 1 decimal place, the highest, the lowest, and the number of days below zero.
  3. Write one program that reads the 7 values in a loop and reports all five results. Use the first-value method for highest and lowest.
  4. Compare the program's results with your hand calculations.
  5. Extension: print a bar for each day with nested loops, one * per degree for warm days and one - per degree below zero.

What to look for: for the sample data, total = −1, average = −0.1, highest = 6, lowest = −7, and 3 days below zero (0 is not below zero). If the highest came out as 0 or the count was 7, check where the accumulators were initialized.

Your turn

1

Write a loop that counts how many numbers from 1 to 50 are multiples of 3 or of 5.

2

Write a loop that adds only the even numbers from 1 to 20.

3

Why should highest not start at 0 when finding the highest temperature?

4

How many times is x printed?

  • for i in range(4):
  •     for j in range(5):
  •         print("x", end="")
5

What is printed?

  • for i in range(1, 4):
  •     for j in range(i):
  •         print(i, end="")
  •     print()
6

Find the bug: the count is always 0 or 1.

  • for n in range(10):
  •     count = 0
  •     if n > 5:
  •         count += 1
  • print(count)
7

Write a program that reads 5 numbers and prints the smallest.

More practice: warm-up

1

Where should an accumulator be initialized: before, inside or after the loop?

2

An outer loop runs 6 times and an inner loop runs 10 times each. How many times does the inner body run?

3

What is the starting value of a counter?

4

What must you check before dividing a total by a count?

5

Which control structure is nested in a loop to count only some values?

More practice: on your own

1

Write a sentinel loop that reads marks until −1, then prints the highest mark, the number of marks, and how many were below 50. (Assume at least one mark is entered.)

2

Write nested loops that print a 5-by-3 rectangle of @ (5 wide, 3 tall).

3

How many times does the innermost line run?

  • for a in range(3):
  •     for b in range(a, 3):
  •         print(a, b)
4

Write a program that prints every pair of whole numbers (x, y) from 1 to 6 with x + y = 7, like two dice.

5

Explain why, in Example 2, total = 0 is inside the outer loop but strong = 0 is outside both loops.

Challenge

1

Write a program that prints every prime number from 2 to 50, using a nested loop to count divisors.

2

Read 6 numbers and print the second highest, assuming all are different. (Hint: keep two records, first and second.)

3

A program has an outer loop over 30 students and an inner loop over 8 tests, plus an if in the inner loop. What is the maximum number of times the if condition is evaluated? If each evaluation takes 1 microsecond, how long would 300 000 students take?

Check your understanding

Quiz

1.

Where should count = 0 go when counting matches in a loop?

2.

Outer loop: range(5). Inner loop: range(4). How many times does the inner body run?

3.

What is the safest starting value for highest when finding the highest of several inputs?

4.

What does this print? for i in range(2): → for j in range(3): → print("*", end=""), then print() inside the outer loop

5.

Which algorithm is NOT one of the nested algorithms named in A2.3?

6.

Why guard total / count with if count > 0:?

For parents and teachers

Big idea: expectation A2.3 asks for algorithms with nested structures, naming counting, totals, highest/lowest values and linear search. This lesson teaches the first three with values read in loops; the unit Arrays and Subprograms applies them, with linear search, to arrays (lists).

How to help: the weather activity is ideal for working side by side: do the hand calculations together, then see whether the program agrees. When results differ, ask "where did you set that variable to 0?"

Common difficulties:

  • Initializing an accumulator inside the loop, or printing results inside the loop.
  • Starting highest at 0 (fails for negative data) or lowest at 0 (fails for positive data).
  • Indenting the row-ending print() too far (inside the inner loop) or not enough (outside both loops).
  • Forgetting to reset a per-student total at the start of each outer iteration.