Sequences Lab: Pipelines with map, filter, and folds

Practice building programs as sequence pipelines (select → transform → combine), predict behavior before running, trace lazy iterator flow, and implement pipelines using fold/accumulate (SICP-inspired).

0. Predict a Pipeline Result (odd squares)

**Predict the output** before running:

```python
def odd_square_sum(n):
    return sum(map(lambda x: x * x,
                   filter(lambda x: x % 2 == 1, range(1, n + 1))))

print(odd_square_sum(10))
print(odd_square_sum(1))
```

What will be printed?

A) `165` then `1`
B) `220` then `1`
C) `165` then `0`
D) Error (because `map`/`filter` are iterators)

  

1. Trace Laziness: Who Gets Pulled From the Source?

This exercise models a *lazy pipeline* by recording events instead of printing.

1) **Trace** which `source` elements are demanded to produce `a = next(it)`.
2) Then trace what happens when we consume the rest with `list(it)`.

```python
def run():
    out = []
    def source():
        for x in [1, 2, 3, 4]:
            out.append(f"source {x}")
            yield x

    it = map(lambda x: x * x,
             filter(lambda x: x % 2 == 0, source()))

    a = next(it)
    out.append(f"a {a}")

    b = list(it)
    out.append(f"b {b}")

    return out
```

**Task**: Before running, write the exact list that `run()` returns.

Then run it to confirm.

  

2. Accumulate Builds Pipelines (SICP Ex 2.33 + Exit Ticket)

In SICP, many sequence operations are built from a single pattern: **accumulation** (fold).

Implement `accumulate` as a **fold-right**:

\[ accumulate(op, z, [a1,a2,...,an]) = op(a1, op(a2, ... op(an, z))) \]

Then use it to build `my_map` and `my_filter` (no explicit loops).

Finally, **extend** it into an exit-ticket pipeline:

> `sum_cubes_div_by_3(n)` returns \( \sum k^3 \) over integers \(1 \le k \le n\) where \(3 \mid k\).

Write your prediction for `sum_cubes_div_by_3(10)` before running tests.