Do you like numbers? Do you often look at yourself in the bathroom mirror and say, "I wish I could have a list of every number in the universe"? You must have been thrilled by the Haskell examples above. Here's more good news: you can do this in Scheme! By using streams, you'll be able to represent infinite sets! Remember, this is possible only because streams use a lazy cons$ to avoid evaluating all their arguments. A stream of integers does not need to compute all the integers it will represent at run-time. It only needs to use cons$ to create the stream, and car$ and cdr$ to look at the stream. Nothing will be generated until it is needed. Here is a function that uses streams to compute the set of all integers:
(define ints$ (lambda (x) (cons$ x (ints$ (add1 x)))))
You can use printn$ or print$ (which you loaded in from the file streams.ss to examine your stream, like this:
> (define zippy (ints$ 1)) > (print$ zippy) 1 2 3 4 5 6 7 8 9 10 Want more? n done > zippy (1 . #)
A good way to produce streams of numbers is to write a function which produces the next number in the stream given the current number. That is the technique used just now in ints$: A call to (ints$ n) does a cons$ of n onto a recursive call to ints$ passing it the next number (+ n 1).
You may be familiar with a result from mathematics that asserts that the positive rational numbers are countable. The proof usually given in mathematics courses involves laying out the rationals in an imagined grid pattern:
This is actually a bit of an overkill, since some rationals appear more than once in slightly different guises. For example 4/2 is the same rational as 2/1 or 6/3. But never mind that.1/1 1/2 1/3 1/4 1/5 1/6 1/7 ... 2/1 2/2 2/3 2/4 2/5 2/6 2/7 ... 3/1 3/2 3/3 3/4 3/5 3/6 3/7 ... 4/1 4/2 4/3 4/4 4/5 4/6 4/7 ... 5/1 5/2 5/3 5/4 5/5 5/6 5/7 ... 6/1 6/2 6/3 6/4 6/5 6/6 6/7 ... 7/1 7/2 7/3 7/4 7/5 7/6 7/7 ... . . . . . . .
To establish that this set of numbers is countable, mathematicians start listing the rationals by following the diagonal paths:
The first diagonal has just one rational number 1/1.
The second has 2/1 and 1/2.
The third has 3/1 2/2 1/3.
The fourth has 4/1 3/2 2/3 1/4.
and so on.
By enumerating the rationals in the order suggested by following the successive diagonals, the mathematicians can be sure that their list covers all the rationals.
So now we can generate the stream of all the positive rationals in this diagonal order:
(define ratsfrom (lambda (r) (cons$ r (ratsfrom (nextrat r))))) (define rats (ratsfrom (cons 1 1)))
The new code should work like this:
> (print$ rats) (1 . 1) (1 . 2) (2 . 1) (1 . 3) (2 . 2) (3 . 1) (1 . 4) (2 . 3) (3 . 2) (4 . 1) Want more? y (1 . 5) (2 . 4) (3 . 3) (4 . 2) (5 . 1) (1 . 6) (2 . 5) (3 . 4) (4 . 3) (5 . 2) Want more? n done >
Here are four functions that work together to give you a list (not a stream!) of prime numbers.
(define filtermult (lambda (n lyst) (if (null? lyst) '() (if (zero? (remainder (car lyst) n)) (filtermult n (cdr lyst)) (cons (car lyst) (filtermult n (cdr lyst)))))))
filtermult makes a list of all the numbers in lyst that are not divisible evenly by n. The built-in function remainder returns the remainder of dividing its first argument by its second argument.
(define sieve (lambda (lyst) (if (null? lyst) '() (cons (car lyst) (sieve (filtermult (car lyst) (cdr lyst)))))))
The function sieve makes a bunch of calls to filtermult in order to remove from lyst any number that is divisible by some other number in lyst. The final result will be a list consisting only of numbers that no other number can divide evenly - the primes.
(define integers (lambda (x y) (if (> x y) '() (cons x (integers (+ 1 x) y)))))
integers generates a list of the integers from x up to y.
(define primes (lambda (m) (sieve (integers 2 m))))
primes returns the results of sieve on the integers from 2 to m.
Here is some sample output.
> (primes 5) (2 3 5) > (primes 25) (2 3 5 7 11 13 17 19 23)
Now it's your turn. Convert these functions to ones that operate on streams rather than lists, so that primes$ will return a stream of all the prime numbers. Call your new functions primes$, integers$, sieve$, and filtermult$.
This is how your function should perform.
> (print$ primes$) 2 3 5 7 11 13 17 19 23 29 Want more? y 31 37 41 43 47 53 59 61 67 71 Want more? y 73 79 83 89 97 101 103 107 109 113 Want more? y 127 131 137 139 149 151 157 163 167 173 Want more? n done >
Observe that if you add, term-by-term, the ints stream to the ones stream, you get the ints stream back again (but offset by one element. This can be viewed as a stream equation.
This can form the basis of a definition of ints.ints = (cons$ 1 (+$ ones ints))
Similarly, here is a way to create the stream of factorials. It relies on the fact that if you take the stream of factorials
and multiply it term-by-term with the stream of positive integers1 1 2 6 24 120 720 ...
you get the stream of factorials back again.1 2 3 4 5 6 7 ...
but offset by one. This motivates the stream equation1 2 6 24 120 720 5040 ...
which forms the basis for my code below.facts = (cons$ 1 (*$ ints facts)),
(define *$ (lambda (s1 s2) (cons$ (* (car$ s1) (car$ s2)) (*$ (cdr$ s1) (cdr$ s2))))) (define ones (cons$ 1 ones)) (define ints (cons$ 1 (+$ ones ints))) (define facts (cons$ 1 (*$ ints facts)))