Practice abstraction barriers with rationals and pairs: predict normalized representations, trace closure-based pairs, and implement rational subtraction that survives a representation swap.
**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)?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.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.