updated the documentation
Added stop-before, stop-after, in-cycle and in-indexed to the documentation. Also clarified (and examplified) the erathostenes example.
This commit is contained in:
parent
6f0c6e636f
commit
66e435aa92
2 changed files with 74 additions and 15 deletions
|
@ -9,18 +9,31 @@
|
|||
<li>A coherent, simple interface for looping over various kinds of data</li>
|
||||
<li>A coherent, simple intefrace for accumulating data in loops, with support for accumulating data over sub-loops</li>
|
||||
<li>A looping facility that in almost all cases produces as fast code as a hand-written named let</li>
|
||||
<li>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.</li>
|
||||
<li>An extensible looping facility, where new ways of iterating over data can be easily added</li>
|
||||
</ul>
|
||||
<p>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.</p>
|
||||
<div id="An example or two"><h3>An example or two</h3><p>So, how does it look? A slightly contrived example, a naive sieve of Erathostenes:</p>
|
||||
<pre class="code-example">
|
||||
(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))
|
||||
</pre><p>Calling <code>(erathostenes 10)</code> returns a list of all primes below 10.</p>
|
||||
<p>The example above can also be written using “subloops”, but unless you know the expansion it can be somewhat surprising.</p>
|
||||
<pre class="code-example">
|
||||
(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)))
|
||||
</pre><p>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.</p>
|
||||
</div></div><div id="Specification"><h2>Specification</h2><p>The loop grammar is the following:</p>
|
||||
<pre class="code-example">
|
||||
(loop [name] (loop-clause ...) [=> final-expr] body ...)
|
||||
|
@ -133,7 +146,11 @@
|
|||
<pre class="code-example">
|
||||
(loop/list (((_ . val) (in-hash hash-table)))
|
||||
val)
|
||||
</pre></dd></dt></dl></div><div id=":acc-clauses"><h3>:acc-clauses</h3><p>Accumulating clauses differ from :for-clauses in 2 significant ways. They have a final value available in the <code>final-expr</code>, 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.</p>
|
||||
</pre></dd></dt><dt><a id="in-cycle"><b>Scheme syntax: </b>in-cycle</a><dd><code>(:for binding (in-cycle iterator))</code><br /><p>Binds the values produced by <code>iterator</code> to <code>binding</code>. Once <code>iterator</code> stops, it starts over.</p>
|
||||
</dd></dt><dt><a id="in-indexed"><b>Scheme syntax: </b>in-indexed</a><dd><code>(:for binding (in-indexed iterator))</code><br /><p>Binds <code>binding</code> to <code>(n . value)</code>, where <code>n</code> is the nth value produced by <code>iterator</code> and <code>value</code> is that value. <code>n</code> starts from 0</p>
|
||||
</dd></dt><dt><a id="stop-before"><b>Scheme syntax: </b>stop-before</a><dd><code>(:for binding (stop-before iterator pred))</code><br /><p>Binds <code>binding</code> to the values produced by <code>iterator</code> until <code>pred</code> 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.</p>
|
||||
</dd></dt><dt><a id="stop-after"><b>Scheme syntax: </b>stop-after</a><dd><code>(:for binding (stop-after iterator pred))</code><br /><p>Binds <code>binding</code> to the values produced by <code>iterator</code> until <code>pred</code> 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.</p>
|
||||
</dd></dt></dl></div><div id=":acc-clauses"><h3>:acc-clauses</h3><p>Accumulating clauses differ from :for-clauses in 2 significant ways. They have a final value available in the <code>final-expr</code>, 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.</p>
|
||||
<p>Another small thing is that for some :acc-clauses, the <code>binding</code> may sometimes only be visible to the user in the <code>final-expr</code>, but like :for-clauses they sometimes offer the programmer to name the loop variables.</p>
|
||||
<p>One general thing about accumulating clauses is that they all support a guarding <code>if</code> form. If such a clause is given, accumulation will only happen if the guard clause returns true. When a <code>:when</code> or <code>:unless</code> clause is given, they also have to return true for any result to be accumulated. The following code returns the empty list:</p>
|
||||
<pre class="code-example">
|
||||
|
|
|
@ -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.
|
||||
|
||||
<subsection title="An example or two">
|
||||
So, how does it look? A slightly contrived example, a naive sieve of Erathostenes:
|
||||
|
||||
<example>
|
||||
(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))
|
||||
</example>
|
||||
|
||||
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.
|
||||
|
||||
<example>
|
||||
(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)))
|
||||
</example>
|
||||
|
||||
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.
|
||||
|
||||
</subsection>
|
||||
</section>
|
||||
|
||||
|
@ -316,6 +334,30 @@
|
|||
val)
|
||||
</example>
|
||||
</syntax>
|
||||
|
||||
<syntax name="in-cycle">
|
||||
<form>(:for binding (in-cycle iterator))</form>
|
||||
|
||||
Binds the values produced by `iterator` to `binding`. Once `iterator` stops, it starts over.
|
||||
</syntax>
|
||||
|
||||
<syntax name="in-indexed">
|
||||
<form>(:for binding (in-indexed iterator))</form>
|
||||
|
||||
Binds `binding` to `(n . value)`, where `n` is the nth value produced by `iterator` and `value` is that value. `n` starts from 0
|
||||
</syntax>
|
||||
|
||||
<syntax name="stop-before">
|
||||
<form>(:for binding (stop-before iterator pred))</form>
|
||||
|
||||
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.
|
||||
</syntax>
|
||||
|
||||
<syntax name="stop-after">
|
||||
<form>(:for binding (stop-after iterator pred))</form>
|
||||
|
||||
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.
|
||||
</syntax>
|
||||
</spec>
|
||||
|
||||
</subsection>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue