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