Solutions to Selected Non-Interpreter Problems CS312 - Fall 2004 P18. -------------------------------------------------- Tree after all nodes have been added: 5 / \ 4 6 / \ 3 7 / \ 2 8 / \ 1 9 Tree after node 1 has been looked up: 1 \ 4 / \ 2 5 \ \ 3 6 \ 7 \ 8 \ 9 Tree after node 9 has been looked up: _____9 / __4___ / \ 1 6 \ / \ 2 5 8 \ / 3 7 P20. -------------------------------------------------- Environment at point 1: ------------- | --------- | | | | | | --------- | ------------- Environment at point 2 (closure for f has no pointer yet): ------------- | --------- | | | | | | --------- | ------------- ^ | ------------- ---------------- | f = ----+---->| fn ... | | ------------- --------------- Environment at point 3 (closure for g has no pointer yet): ------------- | --------- | | | | | | --------- | ------------- ^ | ------------- ---------------- | f = ----+---->| fn ... | | ------------- ---------------- ^ | ------------- ---------------- | g = ----+----------------------->| fn ... | | ------------- ---------------- Environment at point 3 (closure for g has no pointer yet): ------------- | --------- | | | | | | --------- | ------------- ^ | ------------- ---------------- | f = ----+---->| fn ... | . | ------------- -------------+-- ^ | | | ------------- | ---------------- | g = ----+------------------+---->| fn ... | . | ------------- | -------------+-- ^ | | | | | ------------- | | ---------------- | h = ----+------------------+------------------+--->| fn ... | . | ------------- | | -------------+-- ^ ^ ^ | | | | | | | | | | | +--------------------+ | | | +------------------------------------------+ | +---------------------------------------------------------------+ Note: keep in mind that the environment model is a _model_; other reasonable definitions for what happens when mutually recursive functions are possible. The crucial element is that all closures end up having the same environment pointer. P22. -------------------------------------------------- The implementation above is not tail recursive. fun foldr (f: 'a * 'b -> 'b) (acc: 'b) (l: 'a list): 'b = case l of [] => acc | h::t => f(h, foldr t acc t) We now define a tail-recursive version of foldr with the help of a helper function: fun foldr (f: 'a * 'b -> 'b) (acc: 'b) (l: 'a list): 'b = let fun foldl (acc: 'b) (l: 'a list): 'b = case l of [] => acc | h::t => foldl (f(h, acc)) t in foldl acc (rev l) end We have, in fact, implemented foldr as foldl on the reversed argument list. P24. -------------------------------------------------- let val f: int -> int = let val x: int = 312 in fn (_: int)=> 312 end in f 2004 end Remember that for the purposes of the environment model we always deal with code contained (wrapped, encapsulated) by a let statement. This way we avoid having to handle issues related to global scope. Examine the inner let statement. Even the evaluation of the statement ends, the binding for x can not be removed as long as f is in scope. This is because the value of f is a closure, and the closure has a pointer to the environment in which it was defined, and x was part of this environment. P26. -------------------------------------------------- (a) The type of ((((())))) is unit. Parentheses around an expression never change its type. So if type(e) = t, then type(((...(e)...))) = t as well. But note that () is a special notation for unit (there is no e inside the parantheses). (b) The same idea is exploited here: (((((1))))) is of type int. P28. -------------------------------------------------- datatype 'a stream = Null | Stream of 'a * (unit -> 'a stream) fun concat(ss: 'a stream stream): 'a stream = case ss of Null => Null | Stream(h, t) => let fun helper(h: 'a stream): 'a stream = case h of Null => concat (t()) | Stream(h2, t2) => Stream(h2, fn () => helper (t2())) in helper h end P30. -------------------------------------------------- This problem uses the fact that the arithmetic operators + and * are simple functions that can be redefined. let fun op* (n1: int, n2: int): int = if n1 = 39 then 2004 else 312 in (39 * 8, 167 * 12) end