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 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 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>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> | </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> | <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"> | <pre class="code-example"> | ||||||
| (define (erathostenes n) | (define (erathostenes n) | ||||||
| (define vec (make-vector n #t)) |   (define vec (make-vector n #t)) | ||||||
| (loop/list ((:for i (up-from 2 (to n))) |   (loop/list ((:for i (up-from 2 (:to n))) | ||||||
|               (:when (vector-ref vec i))) |               (:when (vector-ref vec i))) | ||||||
|   (loop ((:for j (up-from (* 2 i) (to n) (by 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)) |       (vector-set! vec j #f)) | ||||||
|     i)) |     i)) | ||||||
|         </pre><p>Calling <code>(erathostenes 10)</code> returns a list of all primes below 10.</p> |         </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> | </div></div><div id="Specification"><h2>Specification</h2><p>The loop grammar is the following:</p> | ||||||
| <pre class="code-example"> | <pre class="code-example"> | ||||||
| (loop [name] (loop-clause ...) [=> final-expr] body ...) | (loop [name] (loop-clause ...) [=> final-expr] body ...) | ||||||
|  | @ -133,7 +146,11 @@ | ||||||
| <pre class="code-example"> | <pre class="code-example"> | ||||||
| (loop/list (((_ . val) (in-hash hash-table))) | (loop/list (((_ . val) (in-hash hash-table))) | ||||||
|   val) |   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>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> | <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"> | <pre class="code-example"> | ||||||
|  |  | ||||||
|  | @ -25,7 +25,8 @@ | ||||||
|     * A coherent, simple  intefrace for accumulating data in loops, with support for accumulating data over sub-loops |     * 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 |     * 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 |     * 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"> |     <subsection title="An example or two"> | ||||||
|       So, how does it look? A slightly contrived example, a naive sieve of Erathostenes: |       So, how does it look? A slightly contrived example, a naive sieve of Erathostenes: | ||||||
|  | @ -33,14 +34,31 @@ | ||||||
|       <example> |       <example> | ||||||
|         (define (erathostenes n) |         (define (erathostenes n) | ||||||
|           (define vec (make-vector n #t)) |           (define vec (make-vector n #t)) | ||||||
|         (loop/list ((:for i (up-from 2 (to n))) |           (loop/list ((:for i (up-from 2 (:to n))) | ||||||
|                       (:when (vector-ref vec i))) |                       (:when (vector-ref vec i))) | ||||||
|           (loop ((:for j (up-from (* 2 i) (to n) (by 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)) |               (vector-set! vec j #f)) | ||||||
|             i)) |             i)) | ||||||
|         </example> |         </example> | ||||||
| 
 | 
 | ||||||
|         Calling `(erathostenes 10)` returns a list of all primes below 10. |         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> |     </subsection> | ||||||
|   </section> |   </section> | ||||||
| 
 | 
 | ||||||
|  | @ -316,6 +334,30 @@ | ||||||
|             val) |             val) | ||||||
|         </example> |         </example> | ||||||
|       </syntax> |       </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> |     </spec> | ||||||
| 
 | 
 | ||||||
|     </subsection> |     </subsection> | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Linus
						Linus