Common Lisp the Language, 2nd Edition
Next: Primitives
Up: Optimization
Previous: Defining New
Series
A key feature of Lisp is that variable declarations are strictly
optional. Nevertheless, it is often the case that they are necessary in
situations where efficiency matters. Therefore, it is important that it
be possible for programmers to provide declarations for every
variable in a program. The transformation of series expressions into
loops presents certain problems in this regard, because the loops
created contain variables not evident in the original code. However, if
the information described below is supplied by the user, appropriate
declarations can be generated for all of the loop variables created.
All the explicit variables that are bound in a series expression (for
example, by a let
that is part of the expression) should be
given informative declarations making use of the type specifier
(series
element-type
)
where appropriate.
Informative types should be supplied to series functions (such as
scan
and map-fn
) that have type arguments.
When using scan
it is important to specify the type of
element in the sequence as well as the sequence itself (for example, by
using (vector * integer)
as opposed to merely
vector
). The form
(list
element-type
)
can
be used to specify the type of elements in a list.
If it is appropriate to have a type more specific than
(series t)
associated with the output of #M
,
#Z
, scan-alist
, scan-file
,
scan-hash
, scan-lists-of-lists-fringe
,
scan-lists-of-lists
, scan-plist
,
series
, latch
, or catenate
, then
the form the
must be used to specify this type.
Finally, if the expression computing a non-series argument to a
series variable is neither a variable nor a constant, the
must be used to specify the type of its result.
For example, the declarations in the series expressions below are sufficient to ensure that every loop variable will have an accurate declaration.
(collect-last (choose-if #'plusp (scan '(list integer) data)))
(collect '(vector * float)
(map-fn 'float #'/
(series (the integer (car data)))
(the (series integer) (scan-file f))))
The amount of information the user has to provide is reduced by the
fact that this information can be propagated from place to place. For
instance, the variable holding the output of choose-if
holds a subset of the elements held by the input variable. As a result,
it is appropriate for it to have the same type. When defining a new
series function, the type specifier series-element-type
can
be used to indicate where type propagation should occur.
[Type specifier]
series-element-type
The type specifier
(series-element-type
variable
)
denotes the type of elements in the series held in variable.
Variable must be a variable carrying a series value (for
example, a series argument of a series function).
series-element-type
can be used only in three places: in a
declaration in a let
, mapping
,
producing
, or other binding form in a series expression; in
a declaration in a defun
being used to define a series
function; or in a type argument to a series function. As an example,
consider that collect-last
could have been defined as
follows. The use of series-element-type
ensures that the
internal variable keeping track of the most recent item has the correct
type.
(defun collect-last (items &optional (default nil))
(declare (optimizable-series-function))
(collect-fn '(series-element-type items)
#'(lambda () default)
#'(lambda (old new) new)
items))
Next: Primitives
Up: Optimization
Previous: Defining New
Series
AI.Repository@cs.cmu.edu