Exercise 2: A 3-Level Nested Chain — Possible Solution ==================================================================== THE PROGRAM ------------------------------ var g = 1; { var a = 2; { var b = 3; print g; print a; print b; g = g + a + b; } } print g; RESULT ------------------------------ ['1', '2', '3', '6'] (print g -> 1, print a -> 2, print b -> 3, then g reassigned to 1+2+3=6, and the final print g -> 6) TRACING get('g') FROM THE INNERMOST BLOCK ------------------------------ Three live Environment objects exist at the point `print g;` runs inside the innermost block: innermost.values = {'b': 3.0} innermost.enclosing -> middle middle.values = {'a': 2.0} middle.enclosing -> global global.values = {'g': 1.0} global.enclosing -> None innermost.get('g'): 'g' not in innermost.values -> fall through innermost.enclosing.get('g') -> ask middle middle.get('g'): 'g' not in middle.values -> fall through middle.enclosing.get('g') -> ask global global.get('g'): 'g' in global.values -> return 1.0 That's exactly 2 hops through `enclosing` (innermost -> middle, middle -> global) before the name is found. `get('a')` from the same innermost block needs only 1 hop (innermost -> middle, found there). `get('b')` needs 0 hops -- it's local to the innermost environment that's currently executing. WHY THIS WORKS AS AN ANSWER ------------------------------ The number of hops is exactly the block-nesting depth between where a name is READ and where it was DECLARED -- not the total nesting depth of the program. `b` costs nothing extra because it's declared and read in the same block; `g` costs the most because it's declared at global scope and read three blocks deep. The final reassignment, `g = g + a + b;`, exercises `assign()` making that identical 2-hop walk (innermost -> middle -> global) to find the scope that actually owns `g`, then writes there -- confirmed by the final `print g;` correctly showing 6, not an unchanged 1 the way the chapter's own NaiveEnvironment bug would have produced.