(* 15-212, Spring 2011 *) (* Michael Erdmann *) (* Code for Lecture 19: Streams and Memoization *) (************************************************************************) (* NOTE: The code for this lecture assumes that the code for *) (* lecture 18 has already been loaded: *) (* use "/afs/andrew/course/15/212sp/code/lecture18.sml"; *) (************************************************************************) (* Note that this implementation is NOT semantically *) (* equivalent to the plain (non-memoizing) streams, since *) (* effects will be executed only once in this implementation *) structure BasicMemoStream :> BASIC_STREAM = struct datatype 'a stream = Stream of unit -> 'a front and 'a front = Empty | Cons of 'a * 'a stream fun expose (Stream (d)) = d () fun delay (d) = let val memoCell = ref d (* temporarily... *) fun memoFun () = (* executed only once... *) let val r = d () in (memoCell := (fn () => r); r) end handle exn => (memoCell := (fn () => raise exn) ; raise exn) val _ = (memoCell := memoFun) in Stream (fn () => !memoCell ()) end (* next two don't memoize, since front is already evaluated *) val empty = Stream (fn () => Empty) fun cons (x, s) = Stream (fn () => Cons (x, s)) end; structure MStream :> STREAM = Stream (structure BasicStream = BasicMemoStream); (* notDivides p q ==> true iff q is not a multiple of p *) fun notDivides p q = (q mod p <> 0); fun sieve s = MStream.delay (fn () => sieve' (MStream.expose s)) and sieve' (MStream.Empty) = MStream.Empty | sieve' (MStream.Cons(p, s)) = MStream.Cons (p, sieve (MStream.filter (notDivides p) s)); val primes = sieve (MStream.tabulate (fn i => i+2)); functor TraceStream (structure BasicStream : BASIC_STREAM) :> BASIC_STREAM = struct (* override of function delay *) (* use with caution! *) open BasicStream fun delay (d) = BasicStream.delay (fn () => (TextIO.print "." ; d ())) (* alternative definition without open *) (* type 'a stream = 'a BasicStream.stream datatype front = datatype BasicStream.front val expose = BasicStream.expose fun delay (d) = BasicStream.delay (fn () => (TextIO.print "." ; d ())) val empty = BasicStream.empty val cons = BasicStream.cons *) end; (* Non-memoized traced streams: *) structure BasicTraceStream = TraceStream (structure BasicStream = BasicStream); structure TStream :> STREAM = Stream (structure BasicStream = BasicTraceStream); (* Memoized traced streams: *) structure BasicMemoTraceStream = TraceStream (structure BasicStream = BasicMemoStream); structure MTStream :> STREAM = Stream (structure BasicStream = BasicMemoTraceStream); (*******************) (* Sieve revisited *) (*******************) (******************************************) (* First with Non-Memoized traced streams *) (******************************************) structure S = TStream; fun greater n = S.delay (fn () => greater' n) and greater' n = S.Cons (n, greater (n+1)); fun sieve s = S.delay (fn () => sieve' (S.expose s)) and sieve' (S.Empty) = S.Empty | sieve' (S.Cons(p, s)) = S.Cons (p, sieve (S.filter (notDivides p) s)); val primes = sieve (greater 2); val p20 = S.take(primes, 20); (* Note how the trace tells us everything is recomputed all over. *) (* In particular all the dots are printed again during this evaluation: *) val p20 = S.take(primes, 20); (* And even more dots are printed here: *) val p21 = S.take(primes, 21); (*************************************) (* Next with Memoized traced streams *) (*************************************) structure S = MTStream; fun greater n = S.delay (fn () => greater' n) and greater' n = S.Cons (n, greater (n+1)); fun sieve s = S.delay (fn () => sieve' (S.expose s)) and sieve' (S.Empty) = S.Empty | sieve' (S.Cons(p, s)) = S.Cons (p, sieve (S.filter (notDivides p) s)); val primes = sieve (greater 2); val p20 = S.take(primes, 20); (* Note how the trace tells us nothing is recomputed. *) (* In particular there are no dots printed during this evaluation: *) val p20 = S.take(primes, 20); (* And, only a few dots are printed here, associated with additional *) (* stream exposures needed to compute the next prime: *) val p21 = S.take(primes, 21); (************************************************************************) (* Simple String Matcher *) (* val stringMatch : string -> string -> int stream *) fun stringMatch s p = let val lp = size (p) val ls = size (s) fun sm (i) = Stream.delay (fn () => sm' (i)) and sm' (i) = if i+lp > ls then Stream.Empty else if String.substring (s, i, lp) = p then Stream.Cons (i, sm (i+1)) else sm'(i+1) in sm (0) end (************************************************************************)