Repeating and Remembering

Processing and Changing Lists


Learning Objectives

  • You can write loops that calculate a result or build a new list, choosing a suitable starting value.
  • You can use a boolean flag to remember whether every item passes a condition.
  • You can use indexes to replace items in a list.
  • You can explain how replacing an item differs from changing a list’s length during iteration.
  • You can trace and test list-processing loops, including boundary values and empty lists.

A list remembers several values. A loop can apply the same rule to each of them.

In Chapter 3, you already learned one important loop pattern by counting characters. The patterns in this chapter are extensions of the same idea: initialize some state, process the items one at a time, and update the state when necessary.

The chapter has two main stages:

  1. Processing values and remembering results: calculate a result, answer a question, or build a new list.
  2. Replacing items by position: use indexes when the task requires changing items in an existing list.

Processing Values and Remembering Results

Calculating a Total

Suppose you have several reading-session lengths:

minutes = [25, 40, 15, 30]

To calculate the total, start from zero and add each value:

No files opened Select a file to edit

The output is:

110

The changing state is total:

valuetotal afterwards
2525
4065
1580
30110

This is often called an accumulator pattern because the variable accumulates a result across iterations.

Practise that pattern in plain language. Most of the program is given; write the missing step that updates the total inside the loop.

Write one accumulator step

0 / 5 points

Drag the given blocks into place and write the missing step in the editable box.

Build a program that adds up the reading-session lengths 25 and 40. The list, the starting total, the loop, and the final print are given. Write the step that updates the total, and place it inside the loop body.

The next starter reads words into a list. Calculate how many characters those words contain altogether.

Add up word lengths

0 / 10 points

The starter reads a non-negative number of words, then that many words, one per line. Each word is nonempty and contains no whitespace. The input code is complete and stores the words in words.

Use a direct loop over words to find how many characters the words contain altogether. Store the result in total_characters for the supplied print statement. Its starting value is already provided.

For these inputs:

5
code
planet
sun
loops
program

the output must be:

Total characters: 25

Your program must work for other words and list lengths too. Count every occurrence of a repeated word. If the first input is 0, there are no words to read and the total is zero.

Counting Selected Values

You have already counted selected characters. The same pattern works with list items:

ratings = [5, 2, 4, 5, 3]
recommended_count = 0
for rating in ratings:
if rating >= 4:
recommended_count = recommended_count + 1
print(recommended_count)

The counter works in the same way whether its input is a string or a list. You can reuse the idea by changing what each iteration checks.

Build the same kind of counter in plain language for reading-session lengths. Write the missing step that increases the counter, and place it inside the condition inside the loop.

Count long sessions with natural-language programming

0 / 5 points

Drag the given blocks into place and write the missing step in the editable box.

Build a program that counts how many reading sessions lasted at least 30 minutes. The list of session lengths is 25, 40, and 15. Print only the final count.

Use the loop and the if block. Write the step that increases the counter, and place it inside the if body.

Now use a list of words to answer a different question: how many words contain at least five characters?

Count long words

0 / 10 points

The starter reads a non-negative number of words, then that many words, one per line. Each word is nonempty and contains no whitespace. The input code is complete and stores the words in words.

How many of the words have at least five characters? Use a direct loop over words and store the answer in long_word_count for the supplied print statement.

For these inputs:

5
code
planet
sun
loops
program

the output must be:

Words with at least 5 characters: 3

Your program must work for other words and list lengths too. A word of exactly five characters counts, and each occurrence of a repeated word counts separately. If the first input is 0, there are no words to read. An empty list or a list with no qualifying words has a count of zero.

Finding the Largest Value

For a non-empty list, one straightforward approach is to start with the first item as the current best candidate:

distances = [14, 9, 18, 11]
longest = distances[0]
for distance in distances:
if distance > longest:
longest = distance
print(longest)

The output is 18.

On the first iteration, the first value is compared with itself. That comparison is unnecessary, but harmless. Keeping the loop this way makes the code simple: every value is processed by the same loop.

This version assumes that the list is not empty because distances[0] requires a first item. A program that may receive an empty list must handle that case separately.

The same idea finds the smallest value by reversing the comparison.

Starting from the first item is important. Starting from 0 is not correct for every list:

distances_below_sea_level = [-14, -5, -9]
highest = 0 # Incorrect: 0 is not one of the values.
for distance in distances_below_sea_level:
if distance > highest:
highest = distance
print(highest)

This program prints 0, even though the largest item is -5. None of the negative values can replace the incorrect starting value. In contrast, starting with the first item gives the loop a real candidate and works for all-negative data as well.

Building a New List

A program can collect selected values into a new list:

package_weights = [1.2, 4.5, 2.8, 6.0]
heavy_weights = []
for weight in package_weights:
if weight >= 4:
heavy_weights.append(weight)
print(heavy_weights)

The output is:

[4.5, 6.0]

The stepper shows the two lists separately. Follow how weight receives each input item while only the values that pass the condition are appended to heavy_weights. The original list does not change.

Code

Step 1 of 14

1 / 14
Formatting...

State

main

No variables

Objects

No objects yet

Output

No output yet

Selecting some input items and collecting them in a new list is often called filtering. The pattern has a clear structure:

  1. start with an empty result list;
  2. inspect each input item;
  3. append the items that satisfy the rule.

Filtering is also useful for a small search: inspect each archive title and collect the ones containing a search term. The next exercise keeps the original titles intact while building a separate result list.

Search archive titles

0 / 10 points

The starter reads a non-negative number of archive titles, then that many titles, and finally a search term. Each value is entered on its own line without a visible prompt. The input code is already complete.

Build a separate list called matching_titles containing every title that includes the search term. Ignore letter case when comparing, but preserve the original spelling, order, and repetitions of the titles. Leave titles unchanged.

For these inputs:

4
The Night Train
Signals at Dawn
Midnight Code
The Last Map
night

the output must be:

Matches: ['The Night Train', 'Midnight Code']

Use a direct loop, membership, .lower(), and .append(...) to practise filtering. You can normalize the search term once before the loop. Use the entered search term so the program works for other searches too.

Spaces in the search term are part of the search. For example, " night " matches "A night train" but not "Midnight Code". Preserve those spaces when reading and comparing the term.

An empty archive or a search with no matches produces Matches: []. An empty search term matches every title: the empty string is contained in every string.

The examples so far used different kinds of remembered state. Before comparing them side by side, check that you can choose a suitable starting value for each job.

Explain More Than One Piece of Remembered State

A single loop can update more than one result. Trace each variable separately, then explain how the updates together describe the program’s behavior.

Explain two results from a word list

0 / 10 points

Read this program, then explain in plain language what it does:

words = ["code", "planet", "sun", "loops", "program"]
long_words = []
total_long_characters = 0
for word in words:
if len(word) >= 5:
long_words.append(word)
total_long_characters = total_long_characters + len(word)
print(long_words)
print(total_long_characters)

Explain which words affect the two results, what the program prints, and how the information in long_words differs from the information in total_long_characters.

Remembering Whether Every Check Has Passed

Sometimes a program needs one final yes-or-no result for a whole sequence. A boolean variable can remember whether every item checked so far has passed:

No files opened Select a file to edit

The flag starts as True because no failing value has been found yet. The value -2 makes it False. The later value 8 passes the check, but it must not reset the flag: a passing value does not undo an earlier failure.

An empty list leaves the flag as True because no value failed the check.

Trace the flag in the next exercise, then find a repair for a second version of the loop.

A Map of the Value-Processing Patterns

Each pattern begins by answering the same question:

What should the remembered result be before any items have been processed?

PatternPurposeInitial stateUpdate during the loopResult for an empty sequence
TotalCombine numeric valuestotal = 0Add every value0
Selected countCount matching valuescount = 0Add 1 when an item matches0
Best so farFind a minimum or maximumbest = values[0]Replace it when a better item appearsMust be handled separately
New result listCollect selected or changed valuesresult = []Append an appropriate item[]
Every value passesCheck a condition for the whole sequenceall_pass = TrueSet to False when a value failsTrue: no value failed

The initial state is part of the algorithm, not just setup. For example, 0 is a sensible total before any values are added, but it is not necessarily a value that can serve as a minimum or maximum.

Built-ins and Learning the Pattern

Python provides built-in functions for several common summaries:

total = sum(values)
smallest = min(values)
largest = max(values)

In everyday programs, these built-ins are often the clearest choice. Here, you implement the operations with loops so that you can see how state is initialized, checked, and updated. The same reasoning will later help with calculations for which there is no ready-made built-in.

Like the loop versions that start from values[0], min(values) and max(values) require the empty case to be considered separately. Calling either function with an empty list raises a ValueError.

Pause and Check Your Progress

Before moving on, check that you can answer these two questions:

  1. A list contains only negative values. What starting value would you use when calculating its total, and what would you use when finding its largest value? What changes if the list is empty?
  2. A flag starts as True and becomes False when an item fails a check. What should happen when a later item passes? What result should an empty list give?

If either question is difficult, return to the relevant example and try different values. This is a useful place to take a break; the next section adds a different job for indexes.

Replacing Items by Position

Sometimes You Need the Position

Direct iteration is usually the clearest choice:

for score in scores:
print(score)

But consider a different task:

Reduce every stored price by one.

The loop variable gives you each value, but assigning to that variable does not replace the item in the list:

prices = [10, 15, 8]
for price in prices:
price = price - 1
print(prices)

The list is still:

[10, 15, 8]

The name price was changed during each iteration, but the list items were not.

To replace items, you need their indexes.

Combining range(...) and len(...) for Indexes

In Chapter 3, you used range(len(word)) to visit each valid string position. Lists use the same zero-based positions. Here, those positions let a program replace the stored items rather than only read their values.

So:

range(len(prices))

provides exactly the indexes needed to visit every position in prices.

prices = [10, 15, 8]
for index in range(len(prices)):
prices[index] = prices[index] - 1
print(prices)

The output is:

[9, 14, 7]

This is an index-based loop.

The whole list changes one position at a time. In the stepper, predict which indexed cell will be highlighted after each assignment.

Code

Step 1 of 9

1 / 9
Formatting...

State

main

No variables

Objects

No objects yet

Output

No output yet

The same changes can be summarized in a table:

indexOld valueNew valueprices afterwards
0109[9, 15, 8]
11514[9, 14, 8]
287[9, 14, 7]
Use Indexes When They Serve a Purpose

Do not replace every direct for loop with range(len(...)).

If you only need each value, prefer:

for price in prices:

If you need the position itself, or need to replace items at those positions, an index-based loop may be appropriate:

for index in range(len(prices)):

The goal is not to use the most complicated loop form. The goal is to express the task clearly.


Practise updating list items by position. First trace some updates, then write a loop to clean up a list of readings.

Do Not Change a List’s Length While Traversing It

Replacing list items does not add or remove positions. In this example, the list has three items before, during, and after the loop:

numbers = [2, 4, 6]
for index in range(len(numbers)):
numbers[index] = numbers[index] * 2

Changing the structure of the list being traversed is different. For example, do not run this loop:

numbers = [2, 4, 6]
for number in numbers:
numbers.append(number + 1) # Incorrect: the list keeps growing.

The loop reaches newly appended items and appends still more items. It does not finish normally. Other changes to a list’s length can make items move and cause a loop to skip or revisit them.

A loop over range(len(numbers)) behaves differently: Python determines the range before the loop begins. Appending an item does not extend that range. A loop finishing is therefore not enough to show that it changed the intended list.

When a task needs a different number of items, build a separate result list instead:

numbers = [2, 4, 6]
with_successors = []
for number in numbers:
with_successors.append(number)
with_successors.append(number + 1)
print(with_successors)

The original list remains stable while the loop traverses it. This is the same safe idea used earlier when building heavy_weights.

Replacement or Structural Change?
  • Replacing values[index] changes the item at an existing position without changing the list’s length.
  • Appending or removing items changes the list’s structure. Do not do that to the list currently being traversed; collect the desired items in a separate list.

Verify Repeated State Changes

When a loop produces the wrong result, the final value does not always show which iteration went wrong. A temporary print inside the loop can make its decisions visible.

The following program should count readings that are at least 10. Before running it, predict which readings appear in the debug output and what the final count will be:

readings = [9, 10, 14, 6]
count = 0
for reading in readings:
if reading > 10:
print(f"DEBUG counting {reading}")
count = count + 1
print(f"Readings at least 10: {count}")

Run the program and compare its output with the requirement:

DEBUG counting 14
Readings at least 10: 1

The final line shows that the count is wrong: both 10 and 14 should count. The debug line tells us which value actually reached the update. The missing 10 points us toward the condition, where > excludes the boundary. Change the comparison to >= and run the same input again.

Choose a debug print that answers a particular question. Printing inside the matching branch shows which items cause an update. Printing the remembered state after the loop shows the result before later code uses it. If the updates themselves are suspect, print the current item and the remembered value just after each update.

Try the repaired loop with an empty list, with no matching readings, and with several readings equal to 10. Predict the output before each run. Then remove the temporary debug print; ordinary output should contain only what the program or exercise requires.

Use the same observations when checking code from someone else, including an AI assistant. The loop’s actual decisions should agree with the stated rule.

Check Your Understanding

  1. Why does assigning score = score + 1 inside for score in scores not replace the list item?
  2. When is for index in range(len(values)) preferable to direct iteration?
  3. Why is replacing values[index] different from appending to values while a loop traverses it?
  4. Why can a loop over range(len(values)) stop even when its body appends to values?
  5. How would you test a loop intended to replace negative values with zero? Include boundary values and an empty list.

AI Study Aid

Create a chapter diagram

We're looking into to what extent AI-generated diagrams could help with studying.

Use this study aid to generate an AI-generated visual summary of the material you just studied. Each diagram style emphasizes the content in a different way, so you can choose the focus that feels most useful.

Using the diagram generator is voluntary and does not affect your course progress. We encourage you to try it out and see if it helps, but it's totally up to you! Your answers help us understand how to make better study aids in the future.

Diagrams tried: 0