[racket-dev] [plt] Push #20898: master branch updated
Noel,
Thanks for sharing your code, and for the comments. Let me see if I understand this correctly: the following code should produce a total, a vector whose elements are the partial sums of elements at lower indices than the corresponding element of the input vector, and a vector whose elements are partial sums of elements at the index and lower indices of the input vector, right?
(define (total-and-partial-sums v)
(for/fold/vector ((sum 0))
((i (vector-length v) 2) ;; Index, length, and two result vectors
(x (in-vector v)))
(let ((sum-including-x (+ sum x)))
(values sum-including-x sum sum-including-x)))) ; The last two values are stored in the result vectors, first goes back in the fold
(total-and-partial-sums '#(1 2 3 4))
; => (values 10 (vector 0 1 3 6) (vector 1 3 6 10))
I can see how that would be very useful. I see that you define for/vector using for/fold/vector, so the final body form of for/vector can produce multiple vectors, if the final expression in the body ... produces multiple values. Also, you require naming the index, no? So, the major differences are:
1. In my forms, the length is optional, and distinguished (when provided) from the for-clauses by a keyword argument. In yours, it must be provided as the first for-clause (thus requiring that the index be named).
2. In my forms, the final body expression must produce exactly one value, which becomes the vector element. In yours, the final body expression must produce exactly as many values as the third argument to the length clause (if provided) or 1 (if no third argument is present).
As for difference 1, I think it's nice to be able to avoid providing a length if I don't want to. Sometimes I may not really care about the efficiency of the loop, but will later need a vector, in which case it's a lot less effort to use (for/vector ((i (in-range 3))) ...) than to have to provide a length. I also prefer the "distinguished" #:length keyword instead of shoehorning the length argument into a not-really-for-clause. I'm curious what others think, however.
Difference #2 seems more significant to me. I really like the idea of being able to produce multiple vectors simultaneously---as you say, it can have efficiency benefits and also be really convenient. However, note that the for/list form does not allow multiple values from the last body expression, so there is an argument from consistency to prohibit it in for/vector as well. I'd be open to adding another keyword argument, however, to designate some number of vectors (independently of the presence or absence of the #:length keyword) for the output. I'll put that in the next version, and we can see what other people think. As with the #:length argument, I would prefer this to be a separate keyword---maybe #:num-vecs, maybe something else---rather than trying to fit it into a special for-clause.
Will
On Aug 22, 2010, at 4:11 PM, Noel Welsh wrote:
> On Sun, Aug 22, 2010 at 9:36 PM, Will M. Farr <wmfarr at gmail.com> wrote:
>> Matthew & co,
> ...
>> I'll make sure to throw a syntax error if I see a #:when in the for-clauses, and I think I should give up on the for*/vector #:length variant. I was hoping that you would have some sort of neat trick to keep a running counter during the nested iteration....
>
> I'm pretty sure this will exclude a lot of my code. I'll have to check tomorrow.
>
> My for/vector allows one to construct multiple vectors in one
> iteration. This is fairly useful to efficient code. I also bind the
> length to a name. I thought I checked that this counter was exactly
> the length after the comprehension terminated, but it appears I don't.
> Code is below.
>
> You won't be surprised to hear I prefer my variant ;-). I have uses
> for every feature that I can dreg up if anyone is interested. I'm keen
> to see a for/vector comprehension in Racket.
>
> N.
>
> (define-syntax (for/fold/vector stx)
> (syntax-case stx ()
> [(for/fold/vector
> ([accum-id init-expr] ...)
> ([idx length] for-clause ...)
> body ...)
> (syntax
> (for/fold/vector
> ([accum-id init-expr] ...)
> ([idx length 1] for-clause ...)
> body ...))]
>
> [(for/fold/vector
> ([accum-id init-expr] ...)
> ([idx length n-vectors])
> body ...)
> (syntax
> (let ([l length])
> (for/fold/vector
> ([accum-id init-expr] ...)
> ([idx l n-vectors] [_ (in-range l)])
> body ...)))]
>
> [(for/fold/vector
> ()
> ([idx length n-vectors] for-clause0 for-clause ...)
> body ...)
> (with-syntax ([(v-id ...)
> (datum->syntax
> stx
> (for/list ([i (in-range (syntax->datum (syntax
> n-vectors)))])
> (gensym 'for-vector))
> (syntax n-vectors))]
> [(temp-id ...)
> (datum->syntax
> stx
> (for/list ([i (in-range (syntax->datum (syntax
> n-vectors)))])
> (gensym 'for-vector))
> (syntax n-vectors))])
> (syntax
> (let* ([l length]
> [idx 0]
> [v-id (make-vector l)] ...)
> (begin
> (for (for-clause0 for-clause ...)
> (let-values (([temp-id ...] (let () body ...)))
> (vector-set! v-id idx temp-id) ...
> (set! idx (add1 idx))))
> (values v-id ...)))))]
>
> [(for/fold/vector
> ([accum-id0 init-expr0] [accum-id init-expr] ...)
> ([idx length n-vectors] for-clause0 for-clause ...)
> body ...)
> (with-syntax ([(v-id ...)
> (datum->syntax
> stx
> (for/list ([i (in-range (syntax->datum (syntax
> n-vectors)))])
> (gensym 'for-vector))
> (syntax n-vectors))]
> [(temp-id ...)
> (datum->syntax
> stx
> (for/list ([i (in-range (syntax->datum (syntax
> n-vectors)))])
> (gensym 'for-vector))
> (syntax n-vectors))])
> (syntax
> (let* ([l length]
> [idx 0]
> [v-id (make-vector l)] ...)
> (let-values (([accum-id0 accum-id ...]
> (for/fold ([accum-id0 init-expr0]
> [accum-id init-expr] ...)
> (for-clause0 for-clause ...)
> (let-values (([accum-id0 accum-id ... temp-id ...]
> (let () body ...)))
> (vector-set! v-id idx temp-id) ...
> (set! idx (add1 idx))
> (values accum-id0 accum-id ...)))))
> (values accum-id0 accum-id ... v-id ...)))))]))
>
> (define-syntax (for/vector stx)
> (syntax-case stx ()
> [(for/vector (for-clause ...)
> body ...)
> (syntax (for/fold/vector () (for-clause ...) body ...))]))