26.15 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.17). 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)

[Выражение цикла] 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


[Выражение цикла] 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))


[Выражение цикла] 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


[Выражение цикла] 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


[Выражение цикла] 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