Lecture $4$: Data Abstraction — Lab

Practice abstraction barriers with rationals and pairs: predict normalized representations, trace closure-based pairs, and implement rational subtraction that survives a representation swap.

0. Predict Normalization Results (SICP Ex 2.1)

**Predict the output** (write it down before running).

This `make_rat` reduces by `gcd` and normalizes the sign so the denominator is always positive.

```python
from math import gcd

def make_rat(n, d):
    if d == 0:
        raise ZeroDivisionError
    g = gcd(n, d)
    n //= g
    d //= g
    if d < 0:
        n, d = -n, -d
    return (n, d)

def numer(r):
    return r[0]

def denom(r):
    return r[1]

def add_rat(x, y):
    return make_rat(
        numer(x) * denom(y) + numer(y) * denom(x),
        denom(x) * denom(y),
    )

def rat_str(r):
    return f"{numer(r)}/{denom(r)}"

print(make_rat(2, 4))
print(rat_str(make_rat(1, -2)))
print(rat_str(add_rat(make_rat(1, 3), make_rat(1, 6))))
```

What are the three printed lines (in order)?

  

1. Trace Message-Passing Pairs (SICP Ex 2.4)

This is a *procedural* (closure) representation of pairs.

1) **Predict** what the final `print` outputs.
2) **Trace** the evaluation of `car(p)` using the substitution model: show what `p` is, what function gets called, and which value is returned.

```python
def cons(x, y):
    return lambda m: m(x, y)

def car(z):
    return z(lambda p, q: p)

def cdr(z):
    return z(lambda p, q: q)

p = cons(3, 4)
print(car(p) + cdr(p))
```

Write your trace as comments in the starter code.

  

2. Implement sub_rat and Swap Representations Without Breaking Users

You will do two things:

1) **Implement** `sub_rat(x, y)` using *only* `make_rat`, `numer`, and `denom` (stay above the abstraction barrier).
2) **Extend** the system by implementing `install_closure_repr()` so rationals are represented using *closures*, not tuples — without changing `add_rat`, `mul_rat`, `sub_rat`, or `equal_rat`.

Rules:
- `sub_rat` must not use tuple indexing like `x[0]`.
- After calling `install_closure_repr()`, existing arithmetic functions must still work.

Starter code already provides tuple-based rationals via `install_tuple_repr()`.

**Goal:** The same rational arithmetic code works under both representations.