First, you may want to consider simplifying your predicate functions, because they are unnecessarily verbose. This is equivalent and of better style in my humble opinion:
fun even n = n mod 2 = 0
fun odd n = n mod 2 <> 0
About the Stream Data Type
Since SML has strict evaluation, the traditional list won't do the trick. You must start by defining your own stream data type. A stream is a delayed list.
Your definition of the fibs function seems to imply the existence of such data type:
datatype 'a stream = Empty | Cons of 'a * (unit -> 'a stream)
As you can see an element of type 'a stream can either be Empty or be a Cons containing some value of type 'a and a function that is capable of generating the next stream element.
By this we can differ when that second element is evaluated until we actually invoke the function.
An Infinite Stream of Natural Numbers
For instance, you could define an infinite stream of natural numbers like this:
fun from n = Cons(n, fn () => from(n +1))
val naturals = from(1)
Here naturals is an infinite stream containing all natural numbers. You can see the Cons contains only the first element, and the second element is a function, that, when evaluated, could generate yet another stream element, this time containing 2, and so on.
The Need of a Library of Stream Functions
Evidently, you cannot use this data structure with the traditional list functions. You would need to write your own functions to deal with this data type.
For instance, you could write your own take function, that takes n elements out a stream creating a finite stream out of the original one:
fun take n xs =
if n = 0
then Empty
else case xs of
Empty => Empty
| Cons(h,t) => Cons(h, fn() => take (n-1) (t()))
Or you could create your own filter function to filter elements out of the stream creating yet a new stream in the process.
fun filter f xs =
case xs of
Empty => Empty
| Cons(h,t) => if f(h)
then Cons(h, fn () => filter f (t()))
else filter f (t())
And you would need a zip function that zips the elements of two streams into yet another zipped stream, like so:
fun zip(xs,ys) =
case (xs,ys) of
(Empty,_) => Empty
| (_, Empty) => Empty
| (Cons(h1,t1), Cons(h2,t2)) => Cons( (h1,h2), fn () => zip(t1(),t2()))
You may even like to have a function that converts a finite stream into a list, just for debugging purposes, since lists are simpler to read in the REPL:
fun toList xs =
case xs of
Empty => []
| Cons(h,t) => h::toList(t())
For instance:
toList (take 10 (from 1)) would get the first 10 natural numbers as a list.
filter odd would produce a function that only gets odd elements out of a int stream.
filter even would produce a function that only gets even elements out of a int stream.
- etc,
Infinite Stream of Fibonacci Numbers
Assuming an infinite streams of Fibonacci numbers:
fun fibonacci() =
let
fun fib(a,b) = Cons(a+b, fn() => fib(b,a+b))
in
Cons(0, fn() => fib(0,1))
end
You could now use the filter odd and filter even functions to filter out only even or odd Fibonacci numbers, and then use the zip function with these two results to obtain a zipped stream of (odds,evens) Fibonacci numbers and from the generated stream, you can take out the first 10 elements...
val fibs = fibonacci()
val evens = filter even
val odds = filter odd
val zipped = zip(evens fibs, odds fibs)
...which you can ultimately turn into a list like so:
val r = toList (take 10 zipped)
if C then true else falseis just a convoluted way of sayingC. ;) - Andreas Rossberg