Suggested Solutions to HW 2 1. (a) Using assumption 1 from the notes and the CAML constructor conventions, we'll get the following representations for the different exp constructors: Abs: |-----|-----|-----| | Abs | int | exp | | TAG | | | | |-----|-----|--|--| | V App: |-----|-----|-----| | App | exp | exp | | TAG | | | | | |-----|--|--|--|--| | | V V Var: |-----|-----| | Var | int | | TAG | | |-----|-----| (I'll ignore the issue of headers; in reality, each record will probably have a header word. For the tagged nodes, the tag, which occupies only a few bits, is likely to live in the header rather than in a word by itself.) I won't bother to draw the tree, which is isomorphic to the syntax tree of the expression, with the above representations substituted for the Appropriate syntax tree nodes. The total number of words required is 3 * (# of Abs nodes) + 3 * (# of App nodes) + 2 * (# of Var nodes) = 3 * 4 + 3 * 4 + 2 * 5 = 34. (Of course, you also have to pretend that the source term used integers rather than strings to identify variables.) (b) The key point is that every evaluation of a constructor causes a new heap node to be built; references to existing constructed values just induce a shared pointer to the value. (We assume no special tricks like "hash consing" to reuse existing heap values that hAppen to have the right contents.) Thus, we can just count fresh constructor evalutions; these are marked with a * in the following evaluation sequence. reduce(App(App(Abs("f",Abs("x",App(App(Var"f",Var"x"),Var"x"))), Abs("y",Var"y")), Abs("z",Var"z"))) = App*(reduce(App(Abs("f",Abs("x",App(App(Var"f",Var"x"),Var"x"))), Abs("y",Var"y"))), reduce(Abs("z",Var"z"))) = App*(subst (Abs("y",Var"y")) "f" (Abs("x",App(App(Var"f",Var"x"),Var"x"))), Abs*("z",reduce(Var"z"))) = App*(Abs*("x",subst (Abs("y",Var"y")) "f" (App(App(Var"f",Var"x"),Var"x"))), Abs*("z",Var"z")) = App*(Abs*("x",App*(subst (Abs("y",Var"y")) "f" (App(Var"f",Var"x")), subst (Abs("y",Var"y")) "f" (Var"x"))), Abs*("z",Var"z")) = App*(Abs*("x",App*(App*(subst (Abs("y",Var"y")) "f" (Var"f"), subst (Abs("y",Var"y")) "f" (Var"x")), Var"x")), Abs*("z",Var"z")) = App*(Abs*("x",App*(App*(Abs("y",Var"y"),Var"x"),Var"x")), Abs*("z",Var"z")) Here 3 App and 2 Abs nodes are allocated, for a total of 15 words. Note that the details of the code are important. For example, if I had written the last case of reduce as | Var v -> Var v then executing this case would have created a new Var node rather than reusing the existing one from the argument. 2. Here are the diffs for solutions: (a) 122a138,148 > | Record el -> Vrecord(List.map (fun (l,e) -> (l,eval0 e)) el) > | Select(l, e) -> > begin > match eval0 e with > Vrecord(vl) -> > begin > try List.assoc l vl > with Not_found -> raise Bad_select > end > | _ -> raise Bad_select > end (b) 26a27 > | Let of var * lexp * lexp 140a167,169 > | Let(x,e1,e2) -> > let v = eval0 e1 in > eval (extend env x v) e2 Extra credit: This is a rather general version of Equal that can be applied to any two values. We could be more restrictive by raising an exception instead at the indicated line. But note that we really do need to subsume integer equality in order to have a useful base case for the recursive comparison -- the only other base case is an empty record! 11c11 < (* | Equal (* for records or summands *) *) --- > | Equal (* for records or summands *) 117a119,132 > | Vprim Equal -> > let rec eqv v1 v2 = > match (v1,v2) with > (Vrecord r1,Vrecord r2) -> List.for_all2 eqlv (Sort.list (<=) r1) (Sort.list (<=) r2) > | (Vlabeled (l1,v1), Vlabeled (l2,v2)) -> eqlv (l1,v1) (l2,v2) > | (Vint i1, Vint i2) -> i1 = i2 > | _ -> false (* or: raise an exception *) > and eqlv (l1,v1) (l2,v2) = l1 = l2 && eqv v1 v2 in > begin > match v2 with > Vrecord[(_,v1);(_,v2)] -> > if eqv v1 v2 then eval0 eTrue else eval0 eFalse > | _ -> raise Bad_primarg > end