

                                                                about-series

    NOTES
    about-series - The Series Macro Package

    DESCRIPTION 

Series combine aspects of sequences, streams, and loops.  Like sequences,
series represent totally ordered multi-sets.  In addition, the series
functions have the same flavor as the sequence functions---namely, they
operate on whole series, rather than extracting elements to be processed by
other functions.  For instance, the series expression below computes the sum
of the positive elements in a list.

(COLLECT-SUM (CHOOSE-IF #'PLUSP (SCAN '(1 -2 3 -4)))) => 4

Like streams, series can represent unbounded sets of elements and are
supported by lazy evaluation: Each element of a series is not computed until
it is needed.  For instance, the series expression below returns a list of
the first five even natural numbers and their sum.  The call on SCAN-RANGE
returns a series of all the even natural numbers.  However, since no
elements beyond the first five are ever used, no elements beyond the first
five are ever computed.

(LET ((X (SUBSERIES (SCAN-RANGE :FROM 0 :BY 2) 0 5))) 
  (VALUES (COLLECT X) (COLLECT-SUM X))) => (0 2 4 6 8) and 20

Like sequences and unlike streams, the act of accessing the elements of a
series does not alter the series.  For instance, both users of X above
receive the same elements.

A totally ordered multi-set of elements can be represented in a loop by the
successive values of a variable.  This is extremely efficient, because it
avoids the need to store the elements as a group in any kind of data
structure.  In most situations, series expressions achieve this same high
level of efficiency, because they are automatically transformed into loops
before being evaluated or compiled.  For instance, the first expression
above is transformed into a loop like the following.

(LET ((SUM 0)) 
  (DOLIST (I '(1 -2  3 -4) SUM) 
    (IF (PLUSP I) (SETQ SUM (+ SUM I))))) => 4

A wide variety of algorithms can be expressed clearly and succinctly using
series expressions.  In particular, at least 90 percent of the loops
programmers typically write can be replaced by series expressions that are
much easier to understand and modify, and just as efficient.  From this
perspective, the key feature of series is that they are supported by a rich
set of functions.  These functions more or less correspond to the union of
the operations provided by the sequence functions, the LOOP clauses, and the
vector operations of APL.

Unfortunately, some series expressions cannot be transformed into loops.
This matters, because while transformable series expressions are much more
efficient than equivalent expressions involving sequences or streams,
non-transformable series expressions are much less efficient.  Whenever a
problem comes up that blocks the transformation of a series expression, a
warning message is issued.  Based on the information in the message, it is
usually easy to provide an efficient fix for the problem.

Fortunately, most series expressions can be transformed into loops.  In
particular, pure expressions (ones that do not store series in variables)
can always be transformed.  As a result, the best approach for programmers
to take is to simply write series expressions without worrying about
transformability.  When problems come up, they can be ignored (since they
cannot lead to the computation of incorrect results) or dealt with on an
individual basis.

                         Series Functions

Throughout this chapter the notation S[j] is used to denote the jth element
of the series S.  As in a list or vector, the first element of a series has
the subscript zero.

The # macro character syntax #Z(...) denotes a series that contains the
elements of LIST.  This syntax is also used when printing series.

(CHOOSE-IF #'SYMBOLP #Z(A 2 B)) => #Z(A B)

Series are self-evaluating objects and the series data type is disjoint
from all other types.

                            Scanners

Scanners create series outputs based on non-series inputs.  They either
operate based on some formula (for example, scanning a range of integers)
or enumerate the elements in an aggregate data structure (for example,
scanning the elements in a list).

                              Mapping

By far the most common kind of series operation is mapping.  In cognizance
of this fact, four different ways are provided for specifying mapping:  one
fundamental form (MAP-FN) and three shorthand forms that are more
convenient in particular common situations.


                  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.


                 Conditional and Other Complex Transducers

This section presents a number of complex transducers, including ones that
support conditional computation.


                              Collectors

Collectors produce non-series outputs based on series inputs.  They either
create a summary value based on some formula (the sum, for example) or
collect the elements of a series in an aggregate data structure (such as a
list).

                        Alteration of Series

Series that come from scanning data structures such as lists and vectors
are closely linked to these structures.  The function ALTER can be used to
modify the underlying data structure with reference to the series derived
from it. (Conversely, it is possible to modify a series by destructively
modifying the data structure it is derived from.  However, given the lazy
evaluation nature of series, the effects of such modifications can be very
hard to predict.  As a result, this kind of modification is inadvisable.)

                    Defining New Series Functions

New functions operating on series can be defined just as easily as new
functions operating on any other data type.  However, expressions
containing these new functions cannot be transformed into loops unless a
complete analysis of the functions is available.  Among other things, this
implies that the definition of a new series function must appear before its
first use.


                             Primitives

A large number of series functions are provided, because there are a large
number of useful operations that can be performed on series.  However, this
functionality can be boiled down to a small number of primitive constructs.

COLLECTING-FN embodies the fundamental idea of series computations that
utilize internal state.  It can be used as the basis for defining any
on-line transducer.

UNTIL embodies the fundamental idea of producing a series that is shorter
than the shortest input series.  In particular, it embodies the idea of
computing a bounded series from non-series inputs.  Together with
COLLECTING-FN, UNTIL can be used to define SCAN-FN, which can be used as
the basis for defining all the other scanners.

COLLECT-LAST embodies the fundamental idea of producing a non-series value
from a series.  Together with COLLECTING-FN, it can be used to define
COLLECT-FN, which (with the occasional assistance of UNTIL) can be used as
the basis for defining all the other collectors.

PRODUCING embodies the fundamental idea of preorder computation.  It can be
used as the basis for defining all the other series functions, including
the off-line transducers.

In addition to the above, four primitives are involved with supporting
various specialized aspects of series functions.  Alterability is supported
by the function TO-ALTER and the declaration PROPAGATE-ALTERABILITY.  The
propagation of type information is supported by the type specifier
SERIES-ELEMENT-TYPE.  The best implementation of certain series functions
requires the form ENCAPSULATED.




;; Note the above text was taken from the text in the publically available
;; series package by Dick Waters. 
;; Transcribed by Brad Miller miller@cs.rochester.edu

    SEE ALSO 
    [Unqualified symbols are in the SERIES package]

    series scan-range scan scan-sublists scan-multiple scan-lists-of-lists
    scan-lists-of-lists-fringe scan-alist scan-plist scan-hash 
    scan-symbols scan-file scan-fn scan-fn-inclusive map-fn mapping 
    iterate cotruncate until until-if previous latch collecting-fn
    choose choose-if expand split split-if catenate subseries positions
    mask mingle chunk collect-first collect-last collect-nth 
    collect-length collect-sum collect-max collect-min collect-and
    collect-or collect collect-append collect-nconc collect-alist
    collect-plist collect-hash collect-file collect-fn alter to-alter
    *suppress-series-warnings* optimizable-series-function off-line-port
    series-element-type producing terminate-producing 
    propagate-alterability encapsulated
    about-generators
    about-series-optimization

;Copyright 1989 by the Massachusetts Institute of Technology,
;Cambridge, Massachusetts.

;Permission to use, copy, modify, and distribute this software and its
;documentation for any purpose and without fee is hereby granted,
;provided that this copyright and permission notice appear in all
;copies and supporting documentation, and that the name of M.I.T. not
;be used in advertising or publicity pertaining to distribution of the
;software without specific, written prior permission. M.I.T. makes no
;representations about the suitability of this software for any
;purpose.  It is provided "as is" without express or implied warranty.

;    M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
;    ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
;    M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
;    ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
;    WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
;    ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
;    SOFTWARE.

