Skip to main content

Day 16 - Working with Lists

Outcomes:

Iterative List Processing with for each Loops and Variables

1. Review: Variables, Mutation, and for each Loops (10 minutes)

A. Variables and Mutation

  • Mutable Variables in Pyret:
    • Declare mutable variables with var and update them with :=.
    • Example:
      var total = 0
      spy: total end
      total := total + 5
      spy: total end
    • Key Point: When you update a variable with :=, its binding in the directory changes to the new value.

B. The for each Loop

  • Syntax and Purpose:
    • Pyret’s for each loop iterates over every element of a list, performing side‑effects (such as updating a variable).
    • Syntax:
      for each(elem from some-list):
      <body that uses elem>
      end
  • Example – Summing Numbers:
    var sum = 0
    for each(n from [list: 0, 1, 2, 3]):
    sum := sum + n
    end
    check:
    sum is 6
    end

C. Multi‑Statement Function Bodies with block:

  • Important Syntax Rule:
    • When a function body contains more than one statement, you must begin the body with block:.
    • Example:
      fun my-len(l :: List<Any>) -> Number block:
      var count = 0
      for each(elem from l):
      count := count + 1
      end
      count
      end

2. Structural Problems with Scalar Answers (20 minutes)

TODO: REPLACE THIS WITH A DIFFERENT EXAMPLE! WE JUST DID THIS!

A. Implementing my-sum (Sum of Numbers)

  • Problem: Compute the sum of a list of numbers.
  • Iterative Strategy:
    • Use a mutable variable total starting at 0.
    • For each number in the list, add it to total.
  • Code:
    fun my-sum(l :: List<Number>) -> Number block:
    var total = 0
    for each(n from l):
    total := total + n
    end
    total
    end
  • Exercise:
    • “Predict the output of my-sum([list: 7, 8, 9]).”
    • Expected answer: 24

3. Structural Problems that Transform or Select from Lists (20 minutes)

A. Implementing my-doubles (Doubling Each Element)

  • Problem: Produce a new list with each number doubled.
  • Iterative Strategy:
    • Initialize a mutable variable result as empty.
    • For each element in the input list, append its doubled value using the list concatenation operator +.
  • Code:
    fun my-doubles(l :: List<Number>) -> List<Number> block:
    var result = empty
    for each(n from l):
    result := result + [list: n * 2]
    end
    result
    end
  • Exercise:
    • “What is my-doubles([list: 3, 5, 2])?”
    • Expected answer: [list: 6, 10, 4]

B. Implementing my-str-len (String Lengths)

  • Problem: Given a list of strings, return a list of their lengths.
  • Iterative Strategy:
    • Use a mutable variable result initialized to empty.
    • For each string, calculate string-length(s) and append it.
  • Code:
    fun my-str-len(l :: List<String>) -> List<Number> block:
    var result = empty
    for each(s from l):
    result := result + [list: string-length(s)]
    end
    result
    end
  • Exercise:
    • “Test my-str-len([list: "hi", "there", "mateys"]).”
    • Expected answer: [list: 2, 5, 6]

C. Implementing my-pos-nums (Selecting Positive Numbers)

  • Problem: Return a list containing only the positive numbers.
  • Iterative Strategy:
    • Use a mutable variable result starting as empty.
    • For each number, if it is greater than 0, append it using +.
  • Code:
    fun my-pos-nums(l :: List<Number>) -> List<Number> block:
    var result = empty
    for each(n from l):
    when n > 0:
    result := result + [list: n]
    end
    end
    result
    end
  • Exercise:
    • “What is my-pos-nums([list: 1, -2, 3, -4])?”
    • Expected answer: [list: 1, 3]

D. Implementing my-alternating (Selecting Every Other Element)

  • Problem: Return a list of alternating elements (keep the first, skip the second, etc.).
  • Iterative Strategy:
    • Declare a mutable variable result (initially empty) and a Boolean keep (initially true).
    • For each element in the list, if keep is true, append it using +, then toggle keep (i.e., keep := not(keep)).
  • Code:
    fun my-alternating(l :: List<Any>) -> List<Any> block:
    var result = empty
    var keep = true
    for each(elem from l):
    when keep:
    result := result + [list: elem]
    end
    keep := not(keep)
    end
    result
    end
  • Exercise:
    • “Test my-alternating([list: 1, 2, 3, 4, 5, 6]). What do you get?”
    • Expected answer: [list: 1, 3, 5]

4. Wrap-Up and Discussion (5 minutes)

  • Key Points Review:
    • We use mutable variables (declared with var and updated with :=) as accumulators.
    • The for each loop iterates over each element in a list.
    • When a function’s body has multiple statements, we begin with block: to indicate a sequence of statements.
    • To append two lists, we use the + operator (e.g., result + [list: value]).

5. Optional Challenge (if time permits)

  • Challenge Exercise – my-running-sum:
    • Write a function that computes the running sum of a list of numbers.
    • Iterative Strategy:
      • Use a mutable variable acc (initialized to 0) and result (initialized to empty).
      • For each number, update acc with acc := acc + n and then append acc to result using +.
  • Code Example:
    fun my-running-sum(l :: List<Number>) -> List<Number> block:
    var result = empty
    var acc = 0
    for each(n from l):
    acc := acc + n
    result := result + [list: acc]
    end
    result
    end
  • Exercise:
    • “What is my-running-sum([list: 1, 2, 3, 4, 5])?”
    • Expected answer: [list: 1, 3, 6, 10, 15]