Next: , Previous: , Up: streams   [Index]


1.15.13 Utilities

Streams, being the signature structured data type of functional programming languages, find useful expression in conjunction with higher–order functions. Some of these higher–order functions, and their relationship to streams, are described below.

The identity and constant procedures are frequently useful as the recursive base for maps and folds; (identity obj) always returns obj, and (const obj) creates a procedure that takes any number of arguments and always returns the same obj, no matter its arguments:

(define (identity obj)
  obj)

(define (const obj)
  (lambda x obj))

Many of the stream procedures take a unary predicate that accepts an element of a stream and returns a boolean. Procedure (negate pred) takes a unary predicate and returns a new unary predicate that, when called, returns the opposite boolean value as the original predicate.

(define (negate pred)
  (lambda (x) (not (pred x))))

negate is useful for procedures like stream-take-while that take a predicate, allowing them to be used in the opposite direction from which they were written; for instance, with the predicate reversed, stream-take-while becomes stream-take-until. stream-remove is the opposite of stream-filter:

(define-stream (stream-remove pred strm)
  (stream-filter (negate pred) strm))

A section is a procedure which has been partially applied to some of its arguments; for instance, (double x), which returns twice its argument, is a partial application of the multiply operator to the number 2. Sections come in two kinds:

The procedure lsec takes a procedure and some prefix of its arguments and returns a new procedure in which those arguments are partially applied; the procedure rsec takes a procedure and some reversed suffix of its arguments and returns a new procedure in which those arguments are partially applied:

(define (lsec proc . args)
  (lambda x
    (apply proc (append args x))))

(define (rsec proc . args)
  (lambda x
    (apply proc (reverse (append (reverse args)
                                 (reverse x))))))

Since most of the stream procedures take a stream as their last (rightmost) argument, left sections are particularly useful in conjunction with streams.

(define stream-sum (lsec stream-fold + 0))

Function composition creates a new function by partially applying multiple functions, one after the other. In the simplest case there are only two functions, f and g, composed as (compose f g); the composition can be bound to create a new function, as in:

(define fg (compose f g))

the procedure compose takes one or more procedures and returns a new procedure that performs the same action as the individual procedures would if called in succession:

(define (compose . fns)
  (let comp ((fns fns))
    (cond
      ((null? fns) 'error)
      ((null? (cdr fns)) (car fns))
      (else
        (lambda args
          (call-with-values
            (lambda ()
              (apply
                (comp (cdr fns))
                args))
            (car fns)))))))

compose works with sections to create succinct but highly expressive procedure definitions. The expression to compute the squares of the integers from 1 to 10 given above at stream-unfold could be written by composing stream-map, stream-take-while, and stream-iterate:

((compose
  (lsec stream-map (rsec expt 2))
  (lsec stream-take-while (negate (rsec > 10)))
  (lsec stream-iterate (rsec + 1)))
 1)

Next: , Previous: , Up: streams   [Index]