Streams - Wed, Nov 13

Sequence Operations

map, filter, and reduce express sequence manipulation using compact expressions.

For example, let's sum all primes in an interval .

def sum_primes(a, b):
    total = 0
    x = a
    while x < b:
        if is_prime(x):
            total += x
        x += 1
    return total

This runs in constant space. But what if we wrote instead:

def sum_primes(a, b):
    return sum(filter(is_prime, range(a, b)))

def is_prime(x):
    return x > 1 and all(map(lambda y: x % y, range(2, x)))

This also runs in !

Let's write these functions in Scheme. range and sum aren't defined, so we'll define them along with prime? and sum-primes.

(define (range a b)
  (if (>= a b) nil (cons a (range (+ a 1) b))))

(define (sum s)
  (reduce + s))

(define (prime? x)
  (and (> x 1)
       (null?
        (filter (lambda (y) (= 0 (remainder x y)))
                (range 2 x)))))

(define (sum-primes a b)
  (sum (filter prime? (range a b))))

Here, sum-primes doesn't run in constant space. The reason is, unlike Python's filter function, the scheme filter doesn't work lazily. That, along with the fact that we're interpreting Scheme in Python, leads to slower runtime.

Streams

A stream is a list, but the rest of the list is only computed when needed (lazy lists).

(car (cons 1 nil)) --> 1 (car (cons-stream 1 nil)) --> 1

(cdr (cons 1 nil)) --> () (cdr-stream (cons-stream 1 nil)) --> ()

(cons 1 (cons 2 nil)) (cons-stream 1 (cons-stream 2 nil))

Here, errors only occur when expressions are evaluated.

(cons 1 (cons (/ 1 0) nil)) leads to an ==ERROR==.

(cons-stream 1 (cons-stream (/ 1 0) nil)) evaluates to (1 . #[promise (not forced)])

Let's try this out:

(define (range-stream a b)
  (if (>= a b) nil (cons-stream a (range-stream (+ a 1) b))))

Running (range 1 1000) crashes, but (range-stream 1 1000) is perfectly fine.

Stream ranges are implicit.

Integer Stream

A stream of consecutive integers

(define (int-stream start)
  (cons-stream start (int-stream (+ start 1))))

Stream Processing

The rest of a constant stream is a constant stream.

(define ones (cons-stream 1 ones))

Combine two by separating each into car and cdr:

(define (add-streams s t)
  (cons-stream (+ (car s) (car t))
               (add-streams (cdr-stream s)
                            (cdr-stream t))))

(define ints (cons-stream 1 (add-streams ones ints)))

Higher-Order Stream Functions

For any prime , any larger prime must not be divisible by .

The stream of integers not divisible by any is:

This recurrence is called the Sieve of Eratosthenes.

(define (sieve s)
  (cons-stream
   (car s)
   (sieve (filter-stream
           (lambda (x) (< 0 (remainder x (car s))))
           (cdr-stream s)))))