![](https://secure.gravatar.com/avatar/963c7aeb8ff36cb7f0f484b398105fa5.jpg?s=120&d=mm&r=g)
I think the natural ordering of things means this wont be a problem in 99% of situations. Obviously you cant use mean until the finally clause, but all of your finally's should come after the averaging finally. (iterate:defmacro-clause (averaging expr &optional into var) (let ((avg (or var iterate::*result-var*))) (alexandria:with-unique-names (cnt sum) `(progn (with ,avg) (with ,sum = 0) (with ,cnt = 0) (incf ,cnt) (incf ,sum ,expr) (finally (setf ,avg (if (plusp ,cnt) (/ ,sum ,cnt) 0)) ))))) (iter (for i from 0 to 100) (averaging i into mean) (finally (return mean))) => 50 Expands to: (LET* ((I NIL) (MEAN NIL) (#:SUM4008 0) (#:CNT4007 0)) (BLOCK NIL (TAGBODY (PROGN (SETQ I -1)) LOOP-TOP-NIL (PROGN (SETQ I (+ I 1)) (IF (> I 100) (GO LOOP-END-NIL)) (PROGN (SETQ #:CNT4007 (+ 1 #:CNT4007)) (SETQ #:SUM4008 (+ I #:SUM4008)))) (PROGN) (GO LOOP-TOP-NIL) LOOP-END-NIL (PROGN (SETF MEAN (IF (PLUSP #:CNT4007) (/ #:SUM4008 #:CNT4007) 0)) (RETURN MEAN))) NIL)) Cheers, Russ Tyndall Acceleration.net ----- Original Message ----- From: Robert Goldman [mailto:rpgoldman@sift.info] To: russ@acceleration.net Cc: iterate-devel@common-lisp.net Sent: Tue, 13 Mar 2018 12:27:47 -0500 Subject: Re: Defining AVERAGING clause Wouldn't this technique have trouble with code movement issues? For example, if I have ``` (iter (for x in data-table) (averaging x into mean) (finally (return mean))) ``` I know that's a contrived example, but I think the point is clear: if I'm going to make `averaging` a collector, I'd like to be able to use its result, and if that result is computed in a `finally` block that's invisible and out of my control, I can't do it, can I? There's `finally-protected`, but I think that's the *opposite* of what I need. Best, r On 13 Mar 2018, at 10:08, Russ Tyndall wrote:
Here is an existing "sampling" clause to pull a random sample from a larger data set. The long and short is just use a finally clause, as you would when writing a normal iterate loop.
(iterate:defmacro-clause (sampling expr &optional into var size size) "resevoir sample the input" (let ((sample (or var iterate::*result-var*))) (alexandria:with-unique-names (i sample-size sigil buffer row) `(progn (with ,sample) (with ,sample-size = (or ,size 100)) (with ,buffer = (make-array ,sample-size :initial-element ',sigil)) (with ,i = 0) (if (< ,i ,sample-size) (setf (aref ,buffer ,i) ,expr) (let ((r (random ,i))) (when (< r ,sample-size) (setf (aref ,buffer r) ,expr)))) (incf ,i) (finally ;; convert our sample to a list, but only if we actually took the sample (when (plusp ,i) (setf ,sample (iter (for ,row in-vector ,buffer) (until (eq ,row ',sigil)) (collect ,row)))))))))
Cheers, Russ Tyndall Acceleration.net On 03/13/2018 10:49 AM, Robert Goldman wrote:
I was going to define an |AVERAGING| collector clause for iterate, but I'm not sure how to do it. The obvious thing, it seemed to me, would be to sum the values as I go along, and count them, and then divide the sum by the count when leaving the loop.
But the examples for |DEFMACRO-CLAUSE| in the manual do all of their work while iterating, and there doesn't seem to be an "at-end" hook. Is the kind of thing I would like feasible, and if so, how is it to be done?
thanks! r