Common Lisp the Language, 2nd Edition

Next: Conditional and Other Up: Series Functions Previous: Mapping

## A.2.3. Truncation and Other Simple Transducers

Transducers compute series from series and form the heart of most series expressions. Mapping is by far the most common transducer. This section presents a number of additional simple transducers.

[Function]
`cotruncate &rest series-inputs
``until bools &rest series-inputs
``until-if pred &rest series-inputs`

Each of these functions accepts one or more series inputs S1, ..., Sn as its `&rest` argument and returns n series outputs T1, ..., Tn that contain the same elements in the same order-that is, Ti=Si. Let k be the length of the shortest input Si. `cotruncate` truncates the series so that each output has length k. Let be the position of the first element in the boolean series bools that is not `nil` or, if every element is `nil`, the length of bools. `until` truncates the series so that each output has length `(min k )`. Let be the position of the first element in S1 such that `(pred S1)` is not `nil` or, if there is no such element, the length of S1. `until-if` truncates the series so that each output has length `(min k )`.

    (cotruncate #Z(1 2 -3 4) #Z(a b c))
      => #Z(1 2 -3) and #Z(a b c)
    (until #Z(nil nil t nil) #Z(1 2 -3 4) #Z(a b c))
      => #Z(1 2) and #Z(a b)
    (until-if #'minusp #Z(1 2 -3 4) #Z(a b c))
      => #Z(1 2) and #Z(a b)

[Function]
`previous items &optional (default nil) (amount 1)`

The series returned by `previous` is the same as the input series items except that it is shifted to the right by the positive integer amount. The shifting is done by inserting amount copies of default before items and discarding amount elements from the end of items.

    (previous #Z(10 11 12) 0) => #Z(0 10 11)

[Function]
`latch items &key :after :before :pre :post`

The series returned by `latch` is the same as the input series items except that some of the elements are replaced by other values. `latch` acts like a latch electronic circuit component. Each input element causes the creation of a corresponding output element. After a specified number of non-null input elements have been encountered, the latch is triggered and the output mode is permanently changed.

The `:after` and `:before` arguments specify the latch point. The latch point is just after the `:after`-th non-null element in items or just before the `:before`-th non-null element. If neither `:after` nor `:before` is specified, an `:after` of `1` is assumed. If both are specified, it is an error.

If a `:pre` is specified, every element prior to the latch point is replaced by this value. If a `:post` is specified, every element after the latch point is replaced by this value. If neither is specified, a `:post` of `nil` is assumed.

    (latch #Z(nil c nil d e)) => #Z(nil c nil nil nil)
    (latch #Z(nil c nil d e) :before 2 :post t) => #Z(nil c nil t t)

[Function]
`collecting-fn type init function &rest series-inputs`

The higher-order function `collecting-fn` supports the general concept of a simple transducer with internal state. The type argument is a type specifier indicating the type of values returned by function. The `values` construct can be used to indicate multiple types; however, type cannot indicate zero values. If type indicates m types , then `collecting-fn` returns m series T1, ..., Tm, where Ti has the type `(series )`. The arguments init and function are functions. The remaining arguments (if any) are all series. Let these series be S1, ..., Sn and suppose that Si has the type `(series )`.

The init must be of type `(function () (values ... ))`.

The function must be of type

    (function ( ...   ... ) (values  ... ))

The length of each output is the same as the length of the shortest input. If there are no bounded series inputs, the outputs are unbounded. The elements of the Ti are computed as follows:

    (values T1 ... Tm) ==
      (multiple-value-call function (funcall  init) S1 ... Sn)

    (values T1 ... Tm) ==
      (funcall function T1 ... Tm S1 ... Sn)

If init or function has side effects, it can count on being called in the order indicated by the equations above. However, given the lazy evaluation nature of series, these functions will not be called until their outputs are actually used (if ever). In addition, no assumptions can be made about the relative order of evaluation of these calls with regard to execution in other parts of a given series expression. The second example below computes a series of partial sums of the numbers in an input series. The third example computes two output series: the partial sums of its first input and the partial products of its second input.

    (defun running-averages (float-list)
      (multiple-value-call #'map-fn
        'float #'/
        (collecting-fn '(values float integer)
                       #'(lambda () (values 0.0 0)
                       #'(lambda (s n x) (values (+ s x) (+ n 1))))
                       float-list)))

    (collecting-fn 'integer #'(lambda () 0) #'+ #Z(1 2 3))
      => #Z(1 3 6)

    (collecting-fn '(values integer integer)
                   #'(lambda () (values 0 1))
                   #'(lambda (sum prod x y)
                       (values (+ sum x) (* prod y)))
                   #Z(4 6 8)
                   #Z(1 2 3))
      => #Z(4 10 18) and #Z(1 2 6)

Next: Conditional and Other Up: Series Functions Previous: Mapping