Note that redefining cons to operate on lists and stripping out the fn() => in the thunkifying idiom fn() => EXPR will result in valid SML code for a list version of the stream. Of course, if the stream is infinite, then SML will not be able to evaluate the list to a value.
For these examples, we will use the standard definition of streams.
datatype 'a stream = Null | Cons of 'a * (unit -> 'a stream) fun cons (x) (y) = Cons(x,y)
Fibonacci sequence
fun fibo(a: int, b: int) = cons a (fn () => fibo(b, a + b))
The invariant here is that fibo(x,y) always generates a sequence, starting with x,y that follows the fibonnaci recurrence -- each element is the sum of two previous elements.
fromList
fromList converts a list into a stream.
fun fromList(l: 'a list): 'a stream =
case l of
[] => Null
| h::t => Cons(h, fn () => fromList t)
fromList(l) is the stream version of a function that copies a list into newly-constructed cells.
circular
circular takes a list and defines an infinite stream consisting of an infinite number of occurances of the input list.
fun circular(l: 'a list): 'a stream = case l of [] => Null | h::t => Cons(h, fn () => circular (t @ [h]))
This solution generates an infinite loop by repeatedly calling circular with the same length list. The new list is generated such that the result values are returned in the same order as in the original list.
map
fun map (f: 'a -> 'b) (s: 'a stream): 'b stream =
case s of
Null => Null
| Cons(h, t) => Cons(f(h), (fn () => map f (t())))
takeN
Here is the original definition of takeN, which returns the first n elements of a stream as a list.
fun takeN(s: 'a stream) (n: int): 'a list =
case (s, n) of
(_, 0) => []
| (Null, _) => raise Empty
| (Cons(h, t), n) => h :: (takeN (t()) (n - 1))
As defined above, takeN is restricted to only seeing the first part of the list. The following definition of takeN returns the stream with the first n elements removed.
fun takeUpToN(s: 'a stream) (n: int): ('a list * 'a stream) =
case (s, n) of
(_, 0) => ([], s)
| (Null, _) => ([], s)
| (Cons(h, t), n) =>
let
val (l, s) = takeUpToN (t()) (n - 1)
in
(h :: l, s)
end
fun takeN(s: 'a stream) (n: int): ('a list * 'a stream) =
let
val (l,rest) = takeUpToN s n
in
(* We can avoid this call to length by refactoring takeUpToN *)
if (length l) = n then
(l, rest)
else
raise Empty
end
This version of takeN allows you to conveniently process a stream in large chunks by converting these chunks into lists. To do so, one would invoke takeN with the result stream to extract the next n elements.
sfold: An infinite, moving fold over a stream
Let's look at a variant of fold that is well-defined even for an infinite stream. How can we define fold on an infinite stream? If we return only a single value, then we can only process a finite portion of the stream. Otherwise, we would potentially have to visit all elements of the stream to compute that value, which takes infinite time.
Creating an infinite output stream as the result allows us to define a fold operation that can use all elements in the infinite stream. Each individual element of the output stream can only depend on a finite number of elements in the input stream. However, since the output stream is infinite, we may eventually visit each input node.
One way of defining stream fold is to compute fold over a moving window of length lookahead. For an output element at position k, the value is taken from the fold of the elements at positions k through k + lookahead - 1.
Here is a solution that uses the takeUpToN helper function used in takeN. takeUpToN performs the same operation as takeN, but only with as many elements as available. If we used takeN, then sfold would run into problems near the end of a finite stream.
We could define different semantics from sfold, perhaps disallowing finite streams, or throwing an exception when attempting to evaluate elements near the end of the finite stream.
fun ('a, 'b)
sfold (lookahead: int, f: 'a * 'b -> 'b, acc: 'b, s: 'a stream): 'b stream =
let
val (l, _) = takeUpToN s lookahead
in
Cons(foldr f acc l,
fn() => sfold(lookahead, f, acc, (tl s)))
end