(* CS 334 Assignment #2 Solutions *) (* 1 *) (* exp base power = base ^ power exp: int -> int -> int *) fun exp b p = let (* exploop ans base power returns ans * base ^ power *) fun exploop 0 b a = a | exploop p b a = if p mod 2 = 1 then exploop (p-1) b (a*b) else exploop (p div 2) (b*b) a in exploop p b 1 end (* 2. *) (* a. pre: f is a unary function, startlist is a list, cond is a unary *) (* boolean function *) (* post: returns a list of the elements of startlist which satisfy cond *) (* that have had f applied to them *) (* fn : ('a -> 'b) -> 'a list -> ('a -> bool) -> 'b list *) fun listcomp f startlist cond = let (* extract lst filters the elements of lst to only include elements that satisfy cond *) fun extract nil = nil | extract (x::xs) = if (cond x) then ((f x)::extract xs) else (extract xs) in extract startlist end; (* or *) fun listcomp f [] cond = [] | listcomp f (fst::rest) cond = if cond fst then (f fst)::(listcomp f rest cond) else listcomp f rest cond (* b. *) datatype job = Managerial | Clerical | Manual; type employee = {name:string, age:int, status:job}; (* pre: emp is of type employee *) (* post: returns the name field of employee *) fun getname (emp:employee) = #name(emp); (* pre: emp is of type employee *) (* post: returns true iff age of emp > 60 and status of emp = Managerial *) fun is_ok (emp:employee) = (#age(emp) > 60 andalso #status(emp) = Managerial); (* pre: elist is a list of employees *) (* post: returns a list of names of Managerial employees over age 60 *) (* fn : employee list -> string list *) fun findemps (elist: employee list) = listcomp getname elist is_ok; (* c. pre: g is a binary function, slist1 and slist2 are lists, cond is *) (* a binary boolean function *) (* post: returns a list of elements derived from all pairs of the form *) (* (x,y) where x is in slist1 and y is in slist2 which satisfy cond *) (* and that have had g applied to them *) (*fn: ('a -> 'b -> 'c) -> 'a list -> 'b list -> ('a -> 'b -> bool) -> 'c list*) fun listcomp2 g slist1 slist2 cond = let (* Take care of one element w/ entire second list *) fun extract elt [] = [] | extract elt (x::xs) = if (cond elt x) then (g elt x)::(extract elt xs) else extract elt xs fun repeat [] [] = [] | repeat (x::xs) [] = [] | repeat [] (y::ys) = [] | repeat (x::xs) (y::ys) = (extract x (y::ys))@(repeat xs (y::ys)) in repeat slist1 slist2 end; (* or *) (* This version combines the extract and repeat functions above *) fun listcomp2' g slist1 slist2 cond = let fun filterApp [] elts = [] | filterApp (fst::rest) (x::xs) = if (cond fst x) then (g fst x)::(filterApp (fst::rest) xs) else filterApp (fst::rest) xs | filterApp (fst::rest) [] = filterApp rest slist2 in filterApp slist1 slist2 end; (* 3. pre: astring is an arithmetic expression involving parenthesis *) (* post: balance returns true iff astring is balanced *) (* Because we haven't covered exception handling, that part is optional *) (* fn : string -> bool *) exception empty_stack; datatype 'a stack = Empty | Push of 'a * ('a stack); fun pop Empty = raise empty_stack | pop (Push (x,xs)) = xs; fun top Empty = raise empty_stack | top (Push (x,xs)) = x; fun balance astring = let fun scan Empty nil = true | scan stk nil = false | scan stk (#"("::rest) = scan (Push (#"(",stk)) rest | scan stk (#"{"::rest) = scan (Push (#"{",stk)) rest | scan stk (#"["::rest) = scan (Push (#"[",stk)) rest | scan stk (#")"::rest) = (stk <> Empty) andalso (top stk = #"(") andalso (scan (pop stk) rest) | scan stk (#"]"::rest) = (stk <> Empty) andalso (top stk = #"[") andalso (scan (pop stk) rest) | scan stk (#"}"::rest) = (stk <> Empty) andalso (top stk = #"{") andalso (scan (pop stk) rest) | scan stk (x::xs) = scan stk xs in scan Empty (explode(astring)) end; (* or *) fun balance' astring = let fun scan Empty nil = true | scan stk nil = false | scan stk (#"("::rest) = scan (Push (#"(",stk)) rest | scan stk (#"{"::rest) = scan (Push (#"{",stk)) rest | scan stk (#"["::rest) = scan (Push (#"[",stk)) rest | scan (Push(#"(",stk)) (#")"::rest) = scan stk rest | scan stk (#")"::rest) = false | scan (Push(#"[",stk)) (#"]"::rest) = scan stk rest | scan stk (#"]"::rest) = false | scan (Push(#"{",stk)) (#"}"::rest) = scan stk rest | scan stk (#"}"::rest) = false | scan stk (x::xs) = scan stk xs in scan Empty (explode(astring)) end; (* 4 *) (* Here, we use a couple of auxillary functions to do the work of "unwrapping" AST_NUMs to do the actual computation, and also to propagate any ERRORs that may occur. I prefer to separate division rather than trying to handle the exception. For one, the exact exception raised on division by 0 may be implementation-dependent. So using another ML other than SML, or the current version, would require changes. And raising and handling exceptions is less efficient than conditionals. One neat trick is to subtract the integer from 0 to achieve negation. Otherwise we'd need another helper function. *) datatype arithExp= AST_NUM of int | AST_NEG of arithExp | AST_PLUS of (arithExp * arithExp) | AST_MINUS of (arithExp * arithExp) | AST_PRODUCT of (arithExp * arithExp) | AST_QUOTIENT of (arithExp * arithExp); datatype arithValue = NUM of int | ERROR; local fun apply oper (NUM n) (NUM m) = NUM(oper(n,m)) | apply _ _ _ = ERROR fun divide (NUM n) (NUM m) = if m = 0 then ERROR else NUM(n div m) | divide _ _ = ERROR in fun evaluate (AST_NUM n) = (NUM n) | evaluate (AST_NEG e) = apply op- (NUM 0) (evaluate e) | evaluate (AST_PLUS(e1,e2)) = apply op+ (evaluate e1) (evaluate e2) | evaluate (AST_MINUS(e1,e2)) = apply op- (evaluate e1) (evaluate e2) | evaluate (AST_PRODUCT(e1,e2)) = apply op* (evaluate e1) (evaluate e2) | evaluate (AST_QUOTIENT(e1,e2)) = divide (evaluate e1) (evaluate e2) end