Common Lisp the Language, 2nd Edition

Next: Variable Initializations Up: Loop Previous: End-Test Control

# 26.8. Value Accumulation

Accumulating values during iteration and returning them from a loop is often useful. Some of these accumulations occur so frequently that special loop clauses have been developed to handle them.

The loop keywords `append`, `appending`, `collect`, `collecting`, `nconc`, and `nconcing` designate clauses that accumulate values in lists and return them.

The loop keywords `count`, `counting`, `maximize`, `maximizing`, `minimize`, `minimizing`, `sum`, and `summing` designate clauses that accumulate and return numerical values. [There is no semantic difference between the ``ing'' keywords and their non-``ing'' counterparts. They are provided purely for the sake of stylistic diversity among users. I happen to prefer the non-``ing'' forms-when I use `loop` at all.-GLS]

The loop preposition `into` can be used to name the variable used to hold partial accumulations. The variable is bound as if by the loop construct `with` (see section 26.9). If `into` is used, the construct does not provide a default return value; however, the variable is available for use in any `finally` clause.

You can combine value-returning accumulation clauses in a loop if all the clauses accumulate the same type of data object. By default, the Loop Facility returns only one value; thus, the data objects collected by multiple accumulation clauses as return values must have compatible types. For example, since both the `collect` and `append` constructs accumulate objects into a list that is returned from a loop, you can combine them safely.

    ;;; Collect every name and the kids in one list by using
    ;;; COLLECT and APPEND.
    (loop for name in '(fred sue alice joe june)
          for kids in '((bob ken) () () (kris sunshine) ())
          collect name
          append kids)
       => (FRED BOB KEN SUE ALICE JOE KRIS SUNSHINE JUNE)

[In the preceding example, note that the items accumulated by the `collect` and `append` clauses are interleaved in the result list, according to the order in which the clauses were executed.-GLS]

Multiple clauses that do not accumulate the same type of data object can coexist in a loop only if each clause accumulates its values into a different user-specified variable. Any number of values can be returned from a loop if you use the Common Lisp function `values`, as the next example shows:

    ;;; Count and collect names and ages.
    (loop for name in '(fred sue alice joe june)
          as age in '(22 26 19 20 10)
          append (list name age) into name-and-age-list
          count name into name-count
          sum age into total-age
          finally
            (return (values (round total-age name-count)
                            name-and-age-list)))
       => 19 and (FRED 22 SUE 26 ALICE 19 JOE 20 JUNE 10)

[Loop Clause]
`collect` expr `[into` var] `
``collecting` expr `[into` var]

During each iteration, these constructs collect the value of the specified expression into a list. When iteration terminates, the list is returned.

The argument var is set to the list of collected values; if var is specified, the loop does not return the final list automatically. If var is not specified, it is equivalent to specifying an internal name for var and returning its value in a `finally` clause. The var argument is bound as if by the construct `with`. You cannot specify a data type for var; it must be of type `list`.

Examples:

    ;;; Collect all the symbols in a list.
    (loop for i in '(bird 3 4 turtle (1 . 4) horse cat)
          when (symbolp i) collect i)
       => (BIRD TURTLE HORSE CAT)

    ;;; Collect and return odd numbers.
    (loop for i from 1 to 10
          if (oddp i) collect i)
       => (1 3 5 7 9)

    ;;; Collect items into local variable, but don't return them.
    (loop for i in '(a b c d) by #'cddr
          collect i into my-list
          finally (print my-list)) `;Prints 1 line
    (A C)
       => NIL

[Loop Clause]
`append` expr `[into` var] `
``appending` expr `[into` var] `
``nconc` expr `[into` var] `
``nconcing` expr `[into` var]

These constructs are similar to `collect` except that the values of the specified expression must be lists.

The `append` keyword causes its list values to be concatenated into a single list, as if they were arguments to the Common Lisp function `append`.

The `nconc` keyword causes its list values to be concatenated into a single list, as if they were arguments to the Common Lisp function `nconc`. Note that the `nconc` keyword destructively modifies its argument lists.

The argument var is set to the list of concatenated values; if you specify var, the loop does not return the final list automatically. The var argument is bound as if by the construct `with`. You cannot specify a data type for var; it must be of type `list`.

Examples:

    ;;; Use APPEND to concatenate some sublists.
    (loop for x in '((a) (b) ((c)))
          append x)
       => (A B (C))

    ;;; NCONC some sublists together.  Note that only lists
    ;;; made by the call to LIST are modified.
    (loop for i upfrom 0
          as x in '(a b (c))
          nconc (if (evenp i) (list x) '()))
       => (A (C))

[Loop Clause]
`count` expr `[into` var] `[type-spec]` `
``counting` expr `[into` var] `[type-spec]`

The `count` construct counts the number of times that the specified expression has a non-`nil` value.

The argument var accumulates the number of occurrences; if var is specified, the loop does not return the final count automatically. The var argument is bound as if by the construct `with`.

If `into` var is used, the optional type-spec argument specifies a data type for var. If there is no `into` variable, the optional type-spec argument applies to the internal variable that is keeping the count. In either case it is an error to specify a non-numeric data type. The default type is implementation-dependent, but it must be a subtype of `(or integer float)`.

Example:

    (loop for i in '(a b nil c nil d e)
          count i)
       => 5

[Loop Clause]
`sum` expr `[into` var] `[type-spec]` `
``summing` expr `[into` var] `[type-spec]`

The `sum` construct forms a cumulative sum of the values of the specified expression at each iteration.

The argument var is used to accumulate the sum; if var is specified, the loop does not return the final sum automatically. The var argument is bound as if by the construct `with`.

If `into` var is used, the optional type-spec argument specifies a data type for var. If there is no `into` variable, the optional type-spec argument applies to the internal variable that is keeping the sum. In either case it is an error to specify a non-numeric data type. The default type is implementation-dependent, but it must be a subtype of `number`.

Examples:

    ;;; Sum the elements of a list.

    (loop for i fixnum in '(1 2 3 4 5)
          sum i)
       => 15

    ;;; Sum a function of elements of a list.

    (setq series
          '(1.2 4.3 5.7))
       => (1.2 4.3 5.7)

    (loop for v in series
          sum (* 2.0 v))
       => 22.4

[Loop Clause]
`maximize` expr `[into` var] `[type-spec]` `
``maximizing` expr `[into` var] `[type-spec]` `
``minimize` expr `[into` var] `[type-spec]` `
``minimizing` expr `[into` var] `[type-spec]`

The `maximize` construct compares the value of the specified expression obtained during the first iteration with values obtained in successive iterations. The maximum value encountered is determined and returned. If the loop never executes the body, the returned value is not meaningful.

The `minimize` construct is similar to `maximize`; it determines and returns the minimum value.

The argument var accumulates the maximum or minimum value; if var is specified, the loop does not return the maximum or minimum automatically. The var argument is bound as if by the construct `with`.

If `into` var is used, the optional type-spec argument specifies a data type for var. If there is no `into` variable, the optional type-spec argument applies to the internal variable that is keeping the intermediate result. In either case it is an error to specify a non-numeric data type. The default type is implementation-dependent, but it must be a subtype of `(or integer float)`.

Examples:

    (loop for i in '(2 1 5 3 4)
          maximize i)
       => 5

    (loop for i in '(2 1 5 3 4)
          minimize i)
       => 1

    ;;; In this example, FIXNUM applies to the internal
    ;;; variable that holds the maximum value.

    (setq series '(1.2 4.3 5.7))
       => (1.2 4.3 5.7)

    (loop for v in series
          maximize (round v) fixnum)
       => 6

    ;;; In this example, FIXNUM applies to the variable RESULT.

    (loop for v float in series
          minimize (round v) into result fixnum
          finally (return result))
       => 1

Next: Variable Initializations Up: Loop Previous: End-Test Control