A. Programming Concepts and Skills · Programming Basics: Data, Decisions and LoopsNested 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:
- Read the first value and make it both the highest and the lowest so far.
- 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. - 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.
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.
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 exampleExample 1: A triangle pattern
Write nested loops that print this pattern for n = 4: a row of 1 #, then 2, then 3, then 4.
- The outer loop chooses the row:
for row in range(1, n + 1):. - Row number
row needs row symbols, so the inner loop is for k in range(row):. The inner range depends on the outer variable. - Inside the inner loop:
print("#", end=""). After the inner loop (still inside the outer loop): print(). - 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 exampleExample 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.
- Outer loop: one iteration per student. Inner loop: one iteration per mark.
- 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. - The count of strong students is set to 0 before both loops, because it counts across all students.
- After the inner loop, compute the average, print it, and use an
if to update the count. - 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 funWhy 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 activityA week of weather
You need: a weather app, the newspaper, or a family member's memory of this week's weather; paper and Python.
- 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.
- 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.
- 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.
- Compare the program's results with your hand calculations.
- 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
1Write a loop that counts how many numbers from 1 to 50 are multiples of 3 or of 5.
Sample:
count = 0for n in range(1, 51): if n % 3 == 0 or n % 5 == 0: count += 1print(count)
Output: 23.
2Write a loop that adds only the even numbers from 1 to 20.
total = 0, then for n in range(1, 21): with if n % 2 == 0: total += n inside. Result: 2 + 4 + ... + 20 = 110. (Or range(2, 21, 2) with no if.)
3Why should highest not start at 0 when finding the highest temperature?
If every value is below zero, none would beat 0, and the program would report 0, which was never entered. Start with the first value.
4How many times is x printed?
for i in range(4): for j in range(5): print("x", end="")
5What is printed?
for i in range(1, 4): for j in range(i): print(i, end="") print()
1, 22, 333 on three lines.
6Find the bug: the count is always 0 or 1.
for n in range(10): count = 0 if n > 5: count += 1print(count)
count = 0 is inside the loop, so it resets every iteration. Move it above the for line. Then the output is 4 (6, 7, 8, 9).
7Write a program that reads 5 numbers and prints the smallest.
Sample:
smallest = float(input("Number: "))for i in range(4): x = float(input("Number: ")) if x < smallest: smallest = xprint("Smallest:", smallest)
More practice: warm-up
1Where should an accumulator be initialized: before, inside or after the loop?
2An outer loop runs 6 times and an inner loop runs 10 times each. How many times does the inner body run?
3What is the starting value of a counter?
4What must you check before dividing a total by a count?
5Which control structure is nested in a loop to count only some values?
More practice: on your own
1Write 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.)
Sample:
mark = int(input("Mark (-1 to end): "))highest = markcount = 0below = 0while mark != -1: count += 1 if mark > highest: highest = mark if mark < 50: below += 1 mark = int(input("Mark (-1 to end): "))print(highest, count, below)
For 72, 45, 90, −1 it prints 90 3 1.
2Write nested loops that print a 5-by-3 rectangle of @ (5 wide, 3 tall).
Sample:
for row in range(3): for col in range(5): print("@", end="") print()
The outer loop counts rows (3); the inner loop prints one row (5).
3How many times does the innermost line run?
for a in range(3): for b in range(a, 3): print(a, b)
For a = 0: b = 0, 1, 2 (3 times); a = 1: b = 1, 2 (2 times); a = 2: b = 2 (1 time). Total 6.
4Write a program that prints every pair of whole numbers (x, y) from 1 to 6 with x + y = 7, like two dice.
Sample:
for x in range(1, 7): for y in range(1, 7): if x + y == 7: print(x, y)
It prints 6 pairs: (1, 6), (2, 5), (3, 4), (4, 3), (5, 2), (6, 1).
5Explain why, in Example 2, total = 0 is inside the outer loop but strong = 0 is outside both loops.
Each student needs a fresh total, so it is reset at the start of each student's iteration. strong counts across all students, so it must be set once, before any student is processed; resetting it would lose earlier students.
Challenge
1Write a program that prints every prime number from 2 to 50, using a nested loop to count divisors.
Sample:
for n in range(2, 51): divisors = 0 for d in range(1, n + 1): if n % d == 0: divisors += 1 if divisors == 2: print(n, end=" ")print()
Output: 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47. The divisor counter is reset for each n.
2Read 6 numbers and print the second highest, assuming all are different. (Hint: keep two records, first and second.)
Read the first two values; put the larger in first and the smaller in second. For each of the remaining 4: if x > first, then second = first and first = x; elif x > second, then second = x. After the loop print second. For 4, 9, 2, 7, 11, 5: first ends at 11 and second at 9.
3A 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?
30 × 8 = 240 times. For 300 000 students: 300 000 × 8 = 2 400 000 evaluations × 1 μs = 2.4 seconds. The work grows in proportion to students × tests.
Check your understandingQuiz
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 teachersBig 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.