On 14 Mar 2018, at 11:13, Russ Tyndall wrote:
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.
Agreed. Still, it's kind of an unhappy feeling to be relying on this not very user-controlled behavior, and if some day downstream it goes wrong, that would be problematic. I guess one would want to be able to build `finally` and `initially` clauses with `defmacro-clause` where you could specify whether they come before all end-programmer clauses of the same type, or before all such clauses of the same type. I suppose one could add sorting to the `final-code` and `init-code` in `iter`, but that seems fraught with peril. You're probably right that I should just not worry about this.
(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