Defining AVERAGING clause

Robert Goldman rpgoldman at sift.info
Wed Mar 14 22:24:44 UTC 2018


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 at sift.info]
> To: russ at acceleration.net
> Cc: iterate-devel at 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
>>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mailman.common-lisp.net/pipermail/iterate-devel/attachments/20180314/6a2e36a8/attachment.html>


More information about the iterate-devel mailing list