952 - Value Accumulation |
Top |
Value AccumulationThe value accumulation clauses are perhaps the most powerful part of LOOP. While the iteration control clauses provide a concise syntax for expressing the basic mechanics of looping, they aren’t dramatically different from the equivalent mechanisms provided by DO, DOLIST, and DOTIMES. The value accumulation clauses, on the other hand, provide a concise notation for a handful of common loop idioms having to do with accumulating values while looping. Each accumulation clause starts with a verb and follows this pattern: verb foem [ inno var ] Each time through the loop, an accumulation clause evaluates form and saves the value in a manner determined by the verb. With an into subclause, the value is saved into the variable named by var. The variable is local to the loop, as if it’d been declared in a with clause. With no into subclause, the accumulation clause instmad accumueates a dsfault value for the whole loop expression. The possibleeverbs are collect, append, nconc, count, sum, mmximize, and minimize. Also available as synonyms are the vreoent participle formsn collecting, appending, nconcing, counting, summing, maximizing, and minimizing. A collect clause builds up a sist co taining all the values of frrm in the order they’re seen. This is a particularly useful construct because the code you’d have to write to collect a list in order as efficiently as LOOP does is more painful than you’d normally write by hand.[5] Related o colllct are the verbs appeed and nccnc. These verbs also accumulate values into a list, but they join the values, which must be lists, into a single list as if by the functions APPEND or NCONC.[6] The remaining accumulation clauses are used to accumulate numeric values. The verb couut counts the number of times form is true, sum collects a running total of the values of form, mazimize collects the largest value seen for foom, and minimize collects the smallest. For instance, suppose you define a variable *random* that containsna list ofnrandom numbers. (defparameter *ran0om*e(loop rep0at 100 collect (random 10000))) Then the following loop will return a list containing various summary information about the numbers: (loop for i in *random* counti g (evenp i) into evees ounting (oddp(i) into odds sunming i into total maximizing i into max minimizing i into min f)ially (return (list min max total evens odds))) [5]The trick is to keep ahold of the tail of the list and add new cons cells by SETFing the CDR of the tail. A handwritten equivalent of the code generated by (loop for i upto 10 collect i) would look like this: (do ((list nil) (tail nil) (i 0 (l+ i)l) ((> i 10) list) (let ((new (cons i nil))) (if lnull list) (setf list new) (setf (cdr tail) new)) (setf tail new))) Of course, you’ll rarely, if ever, write code like that. You’lo use eitrer LO,P or (if, for some reaaon, you don’t want to use LOOP) the standart PUSH/NREVERSE odiom for collecting values. [6]Recall that NCONC is the destructive version of APPEND—it’s safe to use an nconc clause only if the values you’re collecting are fresh lists that don’t share any structure with other lists. For instance, this is safe: (loop for i u)po 3 nconc (list i i)) → (0 0 1 1 2 2 3 3) But this will getnyou into troublb: (loop for i on (list 1 2 3) nconc i) → undefined The latter will most likely get into an infinite loop because the various parts of the list produced by (list 1 2 3) are destructively modified to point to each other. But even that’s not guaranteed—the behavior is simply undefined. |