Skip to main content

Skill 3: Iteration with Lists (Pyret)

1. Understanding Mutable Variables with var​

What to Teach​

Mutable variables allow values to be changed during program execution. In Pyret, var creates variables that can be reassigned, unlike regular bindings which are immutable.

Basic Syntax​

var variable-name = initial-value
variable-name := new-value # reassignment uses :=

When to Use var​

In this class, the only place we will really use them is to accumulate values during iteration. While there are other uses, when they are necessary is more advanced than this class will get, and the basic uses cause more confusion and trouble.

Teaching Examples​

Good Example:

var total = 0
var count = 0

# variables can later be updated, i.e., within the body of a for each() loop
total := total + 10
count := count + 1

What to Point Out:

  • var declares a mutable variable
  • := is used for reassignment (not =)
  • Initial value should match the type you'll be accumulating
  • Variable names should describe what they're accumulating

Common Student Mistakes to Watch For​

  1. Using = instead of := for reassignment

    # BAD - using = for reassignment
    var sum = 0
    sum = sum + 5
  2. Trying to reassign regular bindings

    # BAD - regular bindings can't be changed
    total = 0
    total := 10 # total is not mutable

2. Basic for each Loop Structure​

What to Teach​

for each loops iterate through every element in a list, executing code for each element. Combined with mutable variables, they enable incremental construction of results.

Basic Syntax​

for each(element from list):
# do something with element
end

Teaching Examples​

Simple Example:

numbers = [list: 1, 2, 3, 4, 5]
var sum = 0

for each(num from numbers):
sum := sum + num
end

# sum is now 15

More Complex Example:

names = [list: "alice", "bob", "charlie"]
var result = ""

for each(name from names):
result := result + string-upper(name) + " "
end

# result is "ALICE BOB CHARLIE "

What to Point Out:

  • Loop variable (num, name) represents current element
  • Loop body executes once for each list element
  • Mutable variables accumulate results across iterations
  • Loop variable is only accessible inside the loop

Common Student Mistakes to Watch For​

  1. Forgetting to initialize accumulator variables

    # BAD - sum not initialized
    for each(num from numbers):
    sum := sum + num # Error: sum is not defined
    end
  2. Using immutable bindings in loops

    # BAD - can't change total
    total = 0
    for each(num from numbers):
    total := total + num # total is not mutable
    end
  3. Wrong loop variable scope

    # BAD - trying to use loop variable outside loop
    for each(item from my-list):
    # process item
    end
    final-item = item # item not defined here

3. Common Accumulation Patterns​

What to Teach​

Different types of problems require different accumulation strategies. Students should recognize these patterns and choose appropriate initial values and update operations.

Sum Accumulation​

scores = [list: 85, 92, 78, 96, 88]
var total = 0

for each(score from scores):
total := total + score
end

Product Accumulation​

factors = [list: 2, 3, 4, 5]
var product = 1

for each(factor from factors):
product := product * factor
end

Counting Occurrences​

grades = [list: "A", "B", "A", "C", "A", "B"]
var a-count = 0

for each(grade from grades):
if grade == "A":
a-count := a-count + 1
end
end

Building Strings​

words = [list: "hello", "world", "from", "pyret"]
var sentence = ""

for each(word from words):
sentence := sentence + word + " "
end

Finding Maximum/Minimum​

temperatures = [list: 72, 68, 75, 82, 79]
var max-temp = temperatures.first # Start with first element

for each(temp from temperatures):
if temp > max-temp:
max-temp := temp
end
end

What to Point Out:

  • Choose initial values carefully (0 for sum, 1 for product, empty for strings/lists)
  • Use conditionals inside loops for selective processing
  • Multiple variables can be updated in the same loop
  • Consider what happens on the first iteration

Common Student Mistakes to Watch For​

  1. Wrong initial values

    # BAD - wrong starting value for product
    var product = 0
    for each(num from numbers):
    product := product * num
    end
  2. Not handling empty lists

    # BAD - assumes list always has elements
    var max-value = my-list.first

4. Building Complex Data Structures​

What to Teach​

Iteration can construct lists, strings, and other data structures element by element. Students should understand how to build new data from existing data using accumulation patterns.

Building Lists​

numbers = [list: 1, 2, 3, 4, 5]
var squares = empty

for each(num from numbers):
squares := link(num * num, squares)
end

# squares is [list: 25, 16, 9, 4, 1] (reversed order)

Building Lists in Correct Order​

numbers = [list: 1, 2, 3, 4, 5]
var squares = []

for each(num from numbers):
squares := squares + [list: num * num]
end

# squares is [list: 1, 4, 9, 16, 25] (correct order)

Teaching Examples​

Complex Data Construction:

students = [list: "alice", "bob", "charlie", "diana", "eve"]
var long-names = []
var short-names = []
var total-length = 0

for each(name from students):
total-length := total-length + string-length(name)
if string-length(name) > 4:
long-names := link(string-upper(name), long-names)
else:
short-names := link(name, short-names)
end
end

What to Point Out:

  • link adds to front of list (fast but reverses order)
  • List concatenation + maintains order but is slower
  • Multiple data structures can be built simultaneously

4. Functions with Multiple Statements and block​

What to Teach​

When functions contain multiple statements (not including variable definitions), including for each loops with accumulators that need to be returned, they must be wrapped in a block in Pyret.

Basic block Syntax​

fun calculate-sum(numbers :: List<Number>) -> Number:
doc: "calculates sum of all numbers in list"
block:
var total = 0
for each(num from numbers):
total := total + num
end
total # this is the second expression, would trigger error without block
end
where:
calculate-sum([list: 1, 2, 3, 4, 5]) is 15
calculate-sum([list: 10, 20]) is 30
calculate-sum(empty) is 0
calculate-sum([list: -5, 5]) is 0
end

Shorthand fun with block Syntax​

fun calculate-sum(numbers :: List<Number>) -> Number block:
doc: "calculates sum of all numbers in list"
var total = 0
for each(num from numbers):
total := total + num
end
total
where:
calculate-sum([list: 1, 2, 3, 4, 5]) is 15
calculate-sum([list: 10, 20]) is 30
calculate-sum(empty) is 0
calculate-sum([list: -5, 5]) is 0
end

Teaching Examples​

Good Example - Function with Accumulation:

fun count-vowels(words :: List<String>) -> Number block:
doc: "counts total vowels across all words"
var vowel-count = 0
for each(word from words):
for each(char from string-explode(word)):
if string-contains("aeiouAEIOU", char):
vowel-count := vowel-count + 1
else:
nothing
end
end
end
vowel-count
where:
count-vowels([list: "hello", "world"]) is 3
count-vowels([list: "aeiou"]) is 5
count-vowels([list: "xyz"]) is 0
count-vowels(empty) is 0
count-vowels([list: "HELLO", "WORLD"]) is 3
end

Good Example - Building Data Structures:

fun process-grades(scores :: List<Number>) -> List<String> block:
doc: "converts numeric scores to letter grades"
var grades = empty
for each(score from scores):
if score >= 90:
grades := grades + [list: "A"]
else if score >= 80:
grades := grades + [list: "B"]
else if score >= 70:
grades := grades + [list: "C"]
else:
grades := grades + [list: "F"]
end
end
grades
where:
process-grades([list: 95, 85, 75, 65]) is [list: "A", "B", "C", "F"]
process-grades([list: 90, 80, 70]) is [list: "A", "B", "C"]
process-grades([list: 100]) is [list: "A"]
process-grades(empty) is empty
process-grades([list: 50, 60]) is [list: "F", "F"]
end

When block is Required​

  • Functions with for each loops that need to return accumulated values
  • Functions with multiple statements beyond variable definitions
  • Functions that need to perform sequential operations before returning

When block is NOT Required​

# Simple expression - no block needed
fun double(x :: Number) -> Number:
doc: "doubles a number"
x * 2
where:
double(5) is 10
double(0) is 0
double(-3) is -6
end

# Single variable definition + expression - no block needed
fun circle-area(radius :: Number) -> Number:
doc: "calculates area of circle"
pi = 3.14159
pi * radius * radius
where:
circle-area(1) is-roughly 3.14159 within 0.001
circle-area(2) is-roughly 12.56636 within 0.001
circle-area(0) is 0
end

Common Student Mistakes to Watch For​

  1. Forgetting block with loops

    # BAD - missing block
    fun sum-list(numbers :: List<Number>) -> Number:
    var total = 0
    for each(num from numbers):
    total := total + num
    end
    total
    end
  2. Using block unnecessarily

    # BAD - block not needed for simple expression
    fun add-one(x :: Number) -> Number block:
    x + 1
    end
  3. Forgetting to return the accumulator

    # BAD - loop runs but nothing is returned
    fun bad-sum(numbers :: List<Number>) -> Number block:
    var total = 0
    for each(num from numbers):
    total := total + num
    end
    # missing: total
    end

Common Teaching Scenarios​

When Students Ask "Why not just use map/filter?"​

"Iteration with for each gives you more control and lets you build multiple results at once. Sometimes you need that flexibility, especially when accumulating complex state."

When Students Forget to Initialize Variables​

"Think about what value makes sense before the loop starts. For sums, start with 0. For products, start with 1. For finding max, start with the first element."

When Students Mix Up := and =​

"Remember: = creates a new binding, := changes an existing var. If you declared with var, you must update with :=."