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:
vardeclares 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
-
Using
=instead of:=for reassignment# BAD - using = for reassignment
var sum = 0
sum = sum + 5 -
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
-
Forgetting to initialize accumulator variables
# BAD - sum not initialized
for each(num from numbers):
sum := sum + num # Error: sum is not defined
end -
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 -
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
-
Wrong initial values
# BAD - wrong starting value for product
var product = 0
for each(num from numbers):
product := product * num
end -
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:
linkadds 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 eachloops 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
-
Forgetting
blockwith 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 -
Using
blockunnecessarily# BAD - block not needed for simple expression
fun add-one(x :: Number) -> Number block:
x + 1
end -
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 :=."