;;; primitive operations on streams -- just like lists ;;; ;;; empty? -- is it an empty stream? ;;; empty -- an empty stream ;;; (cons-stream x s) -- cons x onto the stream s ;;; (heads s) -- return the first element in the stream ;;; (tails s) -- return the rest of the elements in the stream ;;; like map on lists -- apply the function f to each ;;; element in the stream (define (maps f stream) (if (empty? stream) empty (cons-stream (f (heads stream)) (maps f (tails stream))))) ;;; like filter on lists -- filter out all elements ;;; in the stream that do not satisfy the test (define (filters test stream) (cond ((empty? stream) empty) ((test (heads stream)) (cons-stream (heads stream) (filters test (tails stream)))) (else (filters test (tails stream))))) ;;; return the nth element in the stream (define (nth stream n) (if (= n 1) (heads stream) (nth (tails stream) (- n 1)))) ;;; return the nth tail of a stream (define (nth-tails stream n) (if (= n 1) stream (nth-tails (tails stream) (- n 1)))) ;;; reduce the first n elements of a stream (define (reduces n f accum stream) (if (or (= n 0) (empty? stream)) accum (reduces (- n 1) f (f (heads stream) accum) (tails stream)))) ;;; return the first n elements of a stream as a list (define (stream->list n stream) (if (or (empty? stream) (= n 0)) empty (cons (heads stream) (stream->list (- n 1) (tails stream))))) ;;; combines two infinite streams using a function f (define (combines f s1 s2) (cons-stream (f (heads s1) (heads s2)) (combines f (tails s1) (tails s2)))) ;;; an infinite stream of ones (define ones (cons-stream 1 ones)) ;;; an infinite stream of twos (define twos (maps add1 ones)) ;;; another way to define an infinite stream of twos (define twos (combines add ones ones)) ;;; generates a stream of integers starting from x (define (start-from x) (cons-stream x (start-from (+ 1 x)))) ;;; the whole numbers start from 0 (define wholes (start-from 0)) ;;; an alternative way to define the whole numbers (define wholes (cons-stream 0 (combines + ones wholes))) ;;; the odd wholes obtained by filtering the wholes (define odd-wholes (filters odd? wholes)) ;;; the even wholes obtained by filtering the wholes (define even-wholes (filters even? wholes)) ;;; returns true iff x divides y evenly (define (divides-evenly? x y) (= 0 (modulo y x))) ;;; given x, returns a function on integers y that ;;; returns true iff x does not divide y evenly. (define (not-divides-evenly? x) (lambda (y) (not (divides-evenly? x y)))) ;;; another way to define odds (define odd-wholes (filters (not-divides-evenly? 2) wholes)) ;;; The Sieve algorithm -- invented by Eratosthenes around ;;; 300 BC. This builds a stream of the prime numbers. ;;; We assume the first number in the stream is prime ;;; and then filter out all multiples of that number ;;; from the rest of the list. Then we move on to any ;;; remaining numbers and filter out any numbers that ;;; are multiples of them and so on. (define (sieve stream) (let ((h (heads stream)) (t (tails stream))) (cons-stream h (sieve (filters (not-divides-evenly? h) (tails stream)))))) ;;; So all of the primes are obtained by starting the ;;; sieve on the infinite stream (2 3 4 5 ...) (define primes (sieve (start-from 2))) ;;; a prime predicate (define (prime? n) (define (iter n ps) (or (= n (heads ps)) (and (> n (heads ps)) (iter n (tails ps))))) (iter n primes)) ;; Recall that the Fibonacci sequence is generated as follows: ;; ;; Fib(0) = 0 ;; Fib(1) = 1 ;; Fib(n) = Fib(n-2) + Fib(n-1) (for n >= 2) (define fibs (cons-stream 0 (cons-stream 1 (combines add fibs (tails fibs))))) ;; coerce a rational to a floating-point number -- used below ;; so we can see what's happening. (define (exact-inexact n) (* n 1.0)) ;; We can also use streams to represent (some) infinite-precision ;; floating-point numbers. For instance, we can represent the ;; value 1/3 as a stream (3/10 3/100 3/1000 3/10000 ... ). ;; yields the series (start (f start) (f (f start)) (f (f (f start))) ...) (define (generate f start) (define (iter n) (cons-stream n (iter (f n)))) (iter start)) ;; (10 100 1000 10000 ...) (define powers-of-ten (generate (lambda (x) (* 10 x)) 10)) ;; (3/10 3/100 3/1000 ...) (define one-third-series (combines / (maps (lambda (x) (* 3 x)) ones) powers-of-ten)) ;; sums the first n numbers in a series producing an approximation (define (nth-approx series n) (reduces n + 0 series)) ;; returns an approximation of 1/3 as a floating-point number up to ;; some accuracy (define (nth-approx-one-third n) (exact-inexact (nth-approx one-third-series n))) ;; From Calculus, we know that: ;; pi = 4/1 - 4/3 + 4/5 - 4/7 + 4/9 - 4/11 + 4/13 - 4/15 ... ;; Notice that this is a sum of an infinite series. Obviously, ;; we cannot compute the sum in finite time, but we can use the ;; infinite series to calculate an approximation to pi up to ;; any degree of precision we want. So let's build the series: ;; ;; (4/1 -4/3 4/5 -4/7 4/9 -4/11 4/13 -4/15 ...) ;; (define fours (cons-stream 4 fours)) ;; (4 4 4 4 ...) (define negative-fours (maps - fours)) ;; (-4 -4 -4 -4 ...) ;; Takes streams (x1 x2 x3 ...) and (y1 y2 y3 ...) and produces ;; the interleaved stream (x1 y1 x2 y2 x3 y3 ...) (define (interleaves s1 s2) (cond ((empty? s1) s2) ((empty? s2) s1) (else (cons-stream (heads s1) (cons-stream (heads s2) (interleaves (tails s1) (tails s2))))))) ;; numers = (4 -4 4 -4 4 -4 ...) (define numers (interleaves fours negative-fours)) ;; pi-series = (4/1 -4/3 4/5 -4/7 4/9 ...) (define pi-series (combines / numers odd-wholes)) ;; Return an approximation to pi by summing the first n values in ;; the pi-series and converting to a floating-point number. ;; NOTE: in practice the series we're using converges to pi very, ;; very slowly and so this is not really a good way to approximate ;; pi. I'm just using this to show how elegant it can be to program ;; with streams. (define (nth-approx-pi n) (exact-inexact (nth-approx pi-series n)))