(* 15-212, Spring 2011 *) (* Michael Erdmann *) (* Code for Lecture 9: Continuations *) (************************************************************************) (* Continuations *) fun add (x, y, k) = k (x+y); fun mult (x, y, k) = k (x*y); val direct = (3+4)*(5+6); (* The following is higher-order machine code for add 3 4 r1 add 5 6 r2 mult r1 r2 r3 return r3 *) val bycont = add (3, 4, fn r1 => add (5, 6, fn r2 => mult (r1, r2, fn r3 => r3))); datatype arith = Plus of arith * arith | Mult of arith * arith | Divide of arith * arith | Value of int; (* A direct evaluator. Will raise an exception if dividing by zero. *) (* val eval0 : arith -> int *) fun eval0 (Plus (a1, a2)) = eval0(a1) + eval0(a2) | eval0 (Mult (a1, a2)) = eval0(a1) * eval0(a2) | eval0 (Divide (a1, a2)) = eval0(a1) div eval0(a2) | eval0 (Value (n)) = n; (* An evaluator using continuations. Returns SOME(n) if the value is well-defined, NONE otherwise (i.e., if there is a division by zero). *) (* eval' : arith * (int -> 'a) -> 'a *) (* eval' (a, k) => k (eval0 (a)) *) fun eval' (Plus (a1, a2), k) = eval' (a1, fn r1 => eval' (a2, fn r2 => k (r1+r2))) | eval' (Mult (a1, a2), k) = eval' (a1, fn r1 => eval' (a2, fn r2 => k (r1*r2))) | eval' (Divide (a1, a2), k) = eval' (a1, fn r1 => eval' (a2, fn r2 => if (r2=0) then NONE else k(r1 div r2))) | eval' (Value (n), k) = k (n); (* val eval : arith -> int option *) fun eval a = eval' (a, fn r => SOME r); val a77 = Mult (Plus (Value (3), Value (4)), Plus (Value (5), Value (6))); val seventySeven0 = eval0 (a77); val seventySeven = eval (a77); val abad = Mult (Plus (Value (3), Value (4)), Divide (Value (5), Value (0))); (* Uncomment the next expression to see it raise the exception divide by zero *) (* val valbad0 = eval0 (abad); *) (* This should return NONE *) val valbad = eval (abad); (***************************************************************) (* A tree datatype, with integers stored in internal nodes. *) datatype tree = Empty | Node of int * tree * tree; (* Sample: *) val t1 = Node(4, Node(2, Node(1, Empty, Empty), Node(3, Empty, Empty)), Node(5, Empty, Empty)); (* 4 / \ 2 5 / \ 1 3 *) (* The following function generates a list corresponding to the inorder traversal of a tree, using tail-recursion: val inorder : tree * int list --> int list *) fun inorder(Empty, acc) = acc | inorder(Node(n, left, right), acc) = inorder(left, n::inorder(right, acc)); (* The following function decides whether the inorder traversal of the given tree is the given list. The function relies on "inorder". Specifically, the function first creates a list representing the inorder traversal of the tree, then it compares that list against the given list. val treematch : tree -> int list -> bool *) fun treematch t l = (inorder(t, nil) = l); treematch t1 [1, 2, 3, 4]; treematch t1 [1, 2, 3, 4, 5]; treematch t1 [1, 2, 3, 4, 5, 6]; (**************************************************) (* Here now, for practice, is a continuation-based version. NOTE: In this version, we peel off elements from the given list as we are doing the tree traversal. *) (* The following function decides whether the inorder traversal of a tree corresponds to a prefix of a list. val prefix : tree -> int list -> (int list -> bool) -> bool prefix t l k ==> { true, if l = l1 @ l2, such that { the inorder traversal of t is equal to l1, { and k(l2) = true. { { false, otherwise. *) fun prefix (Empty) l k = k(l) | prefix (Node(n, left, right)) l k = prefix left l (fn nil => false | a::l' => (n=a) andalso (prefix right l' k)); (* NOTE: prefix may look slightly complicated, but this basic format will be very useful to us when we look at regular expressions later this week. *) (* The following function decides whether the inorder traversal of the given tree is the given list. The function relies on "prefix". val treematch' : tree -> int list -> bool *) fun treematch' t l = prefix t l List.null; treematch' t1 [1, 2, 3, 4]; treematch' t1 [1, 2, 3, 4, 5]; treematch' t1 [1, 2, 3, 4, 5, 6]; (************************************************************************)