diff --git a/documentation/doc.html b/documentation/doc.html index 36910ab..7cf1d18 100644 --- a/documentation/doc.html +++ b/documentation/doc.html @@ -9,18 +9,31 @@
The only other lisp looping facility that I know of that provides these things is Common Lisps iterate package. Iterate does however do a lot of things that are cumbersome to do in portable scheme, and would be prohibitively complicated to implement efficiently in a portable way. Unless, of course, one considers CPS-conversion of arbitrary scheme code using syntax-rules simple.
So, how does it look? A slightly contrived example, a naive sieve of Erathostenes:
(define (erathostenes n) -(define vec (make-vector n #t)) -(loop/list ((:for i (up-from 2 (to n))) - (:when (vector-ref vec i))) - (loop ((:for j (up-from (* 2 i) (to n) (by i)))) - (vector-set! vec j #f)) - i)) + (define vec (make-vector n #t)) + (loop/list ((:for i (up-from 2 (:to n))) + (:when (vector-ref vec i))) + ;; Here we set all multiples of i to #f + (loop ((:for j (up-from (* 3 i) (:to n) (:by (* i 2))))) + (vector-set! vec j #f)) + i))
Calling (erathostenes 10)
returns a list of all primes below 10.
The example above can also be written using “subloops”, but unless you know the expansion it can be somewhat surprising.
++(define (erathostenes n) + (define vec (make-vector n #t)) + (loop ((:for i (up-from 2 (:to n))) + (:acc lst (listing i)) + (:when (vector-ref vec i)) + (:for j (up-from (* 3 i) (:to n) (:by (* i 2)))) + => lst + (vector-set! vec j #f))) +
Any :for clause following a :break, :when, :unless or :final clause is considered to be a subloop. Any :when clause also affects when accumulating clauses collect values. The expression following => is the final expression: this is the expression returned after the loop ends.
The loop grammar is the following:
(loop [name] (loop-clause ...) [=> final-expr] body ...) @@ -133,7 +146,11 @@(loop/list (((_ . val) (in-hash hash-table))) val) -
Accumulating clauses differ from :for-clauses in 2 significant ways. They have a final value available in the final-expr
, and they keep their state throughout the loop. In the case of a loop with one subloop, the :for-clauses reset their state every time the subloop is entered. :acc-clauses will always keep their state.
(:for binding (in-cycle iterator))
Binds the values produced by iterator
to binding
. Once iterator
stops, it starts over.
(:for binding (in-indexed iterator))
Binds binding
to (n . value)
, where n
is the nth value produced by iterator
and value
is that value. n
starts from 0
(:for binding (stop-before iterator pred))
Binds binding
to the values produced by iterator
until pred
applied to that value returns true. The iterator is then considered exhausted. Useful in subloops where one might want to end internal iteration without :break-ing.
(:for binding (stop-after iterator pred))
Binds binding
to the values produced by iterator
until pred
applied to that value returns true. It then produces that last value. The iterator is then considered exhausted. Useful in subloops where one might want to end internal iteration without :break-ing.
Accumulating clauses differ from :for-clauses in 2 significant ways. They have a final value available in the final-expr
, and they keep their state throughout the loop. In the case of a loop with one subloop, the :for-clauses reset their state every time the subloop is entered. :acc-clauses will always keep their state.
Another small thing is that for some :acc-clauses, the binding
may sometimes only be visible to the user in the final-expr
, but like :for-clauses they sometimes offer the programmer to name the loop variables.
One general thing about accumulating clauses is that they all support a guarding if
form. If such a clause is given, accumulation will only happen if the guard clause returns true. When a :when
or :unless
clause is given, they also have to return true for any result to be accumulated. The following code returns the empty list:
diff --git a/documentation/doc.xml b/documentation/doc.xml index 08761fa..3b9410b 100644 --- a/documentation/doc.xml +++ b/documentation/doc.xml @@ -25,22 +25,40 @@ * A coherent, simple intefrace for accumulating data in loops, with support for accumulating data over sub-loops * A looping facility that in almost all cases produces as fast code as a hand-written named let * An extensible looping facility, where new ways of iterating over data can be easily added - The only other lisp looping facility that I know of that provides these things is Common Lisps iterate package. Iterate does however do a lot of things that are cumbersome to do in portable scheme, and would be prohibitively complicated to implement efficiently in a portable way. + + The only other lisp looping facility that I know of that provides these things is Common Lisps iterate package. Iterate does however do a lot of things that are cumbersome to do in portable scheme, and would be prohibitively complicated to implement efficiently in a portable way. Unless, of course, one considers CPS-conversion of arbitrary scheme code using syntax-rules simple.So, how does it look? A slightly contrived example, a naive sieve of Erathostenes: @@ -316,6 +334,30 @@ val) + +(define (erathostenes n) - (define vec (make-vector n #t)) - (loop/list ((:for i (up-from 2 (to n))) - (:when (vector-ref vec i))) - (loop ((:for j (up-from (* 2 i) (to n) (by i)))) - (vector-set! vec j #f)) - i)) + (define vec (make-vector n #t)) + (loop/list ((:for i (up-from 2 (:to n))) + (:when (vector-ref vec i))) + ;; Here we set all multiples of i to #f + (loop ((:for j (up-from (* 3 i) (:to n) (:by (* i 2))))) + (vector-set! vec j #f)) + i)) Calling `(erathostenes 10)` returns a list of all primes below 10. + + The example above can also be written using "subloops", but unless you know the expansion it can be somewhat surprising. + ++ (define (erathostenes n) + (define vec (make-vector n #t)) + (loop ((:for i (up-from 2 (:to n))) + (:acc lst (listing i)) + (:when (vector-ref vec i)) + (:for j (up-from (* 3 i) (:to n) (:by (* i 2)))) + => lst + (vector-set! vec j #f))) + + + Any :for clause following a :break, :when, :unless or :final clause is considered to be a subloop. Any :when clause also affects when accumulating clauses collect values. The expression following => is the final expression: this is the expression returned after the loop ends. ++ + + Binds the values produced by `iterator` to `binding`. Once `iterator` stops, it starts over. + + ++ + + Binds `binding` to `(n . value)`, where `n` is the nth value produced by `iterator` and `value` is that value. `n` starts from 0 + + ++ + + Binds `binding` to the values produced by `iterator` until `pred` applied to that value returns true. The iterator is then considered exhausted. Useful in subloops where one might want to end internal iteration without :break-ing. + + ++ + + Binds `binding` to the values produced by `iterator` until `pred` applied to that value returns true. It then produces that last value. The iterator is then considered exhausted. Useful in subloops where one might want to end internal iteration without :break-ing. +