Changed accumulator test/bind order to bind/test
This means vectoring exits directly when the index loop variable = :length. It also means :final has to change.
This commit is contained in:
parent
aa77fef2ad
commit
7a1137e579
3 changed files with 71 additions and 66 deletions
123
README.md
123
README.md
|
@ -1,78 +1,32 @@
|
|||
# goof-loop - a scheme looping facility
|
||||
|
||||
goof-loops aims to be an amalgamation of the racket for loops and Alex Shinn's (chibi-loop). We are many that found racket's for loops a breeze of fresh air, but in the end their most general forms (for/fold and for/foldr) are kinda odd to work with. If you choose not to use those general for loops, you cannot express arbitrary transformations, like say a fibonacci sequence, since for clauses cannot reference eachother. goof-loop tries to fix this.
|
||||
goof-loops aims to be an amalgamation of the racket for loops and Alex Shinn's (chibi-loop). We are many that found racket's for loops a breeze of fresh air, but in the end their most general forms (for/fold and for/foldr) are kinda odd to work with. If you choose not to use those general for loops, you cannot express arbitrary transformations, like say a fibonacci sequence, since for clauses cannot reference eachother. goof-loop tries to fix this.
|
||||
|
||||
Compared to foof-loop, some things are added. Apart from minor syntactic changes, subloops are supported. The best way is to show:
|
||||
|
||||
```
|
||||
``` scheme
|
||||
(define lst '((1 2) dud (3 4) (5 6)))
|
||||
(loop ((:for a (in-list lst))
|
||||
(:when (pair? a))
|
||||
(:for b (in-list a))
|
||||
(:acc acc (summing b)))
|
||||
=> acc)
|
||||
;; => 21
|
||||
```
|
||||
|
||||
This will sum all the sublists of lst and produce the result 21. Any :when, :unless, :break, :final, or :subloop clause will break out a subloop if any subsequent for clauses are found.
|
||||
Any :when, :unless, :break, :final, :bind, :do or :subloop clause will break out a subloop if any subsequent for clauses are found.
|
||||
|
||||
## Beta warning
|
||||
|
||||
This is beta quality software, and some minor details are likely to change. I have gotten most kinks worked out though.
|
||||
This is beta quality software, and some minor details are likely to change. I have gotten most kinks worked out though, but if I ever figure out how to include branching, the body-part of the macro will become a :body clause.
|
||||
|
||||
## Documentation
|
||||
|
||||
The current WIP documentation can be found here: https://bjoli.srht.site/doc.html
|
||||
The current WIP documentation can be found here: https://bjoli.srht.site/doc.html (WARNING: for 0.1, not master)
|
||||
|
||||
It is written in a weird markdown/xml chimaera. You can find it in documentation doc.xml (for the weird format) and documentation/doc.html for the slightly more accessible HTML format.
|
||||
|
||||
|
||||
## Differences from foof-loop
|
||||
|
||||
### lexical
|
||||
|
||||
foof-loop has a lot of code movement going on, and it can be hard to understand exactly where things end up. goof employs a more strict lexical hierarchy. The following is not possible in (chibi loop):
|
||||
d into only after the above clauses have been evaluated.
|
||||
|
||||
### syntactical
|
||||
|
||||
for-clauses are split into :for and :acc clauses. This is because the addition of subloops means we have to treat accumulators differently.
|
||||
|
||||
while and until are removed in favour of :break.
|
||||
|
||||
:when and :unless are added to better control when the loop body is executed (and accumulators accumulated)
|
||||
|
||||
with-clauses are removed in favour of (:for var (in init [step [stop]])) in case of loop clauses, or (:acc var (folding init [step])) in case of accumulators.
|
||||
|
||||
### Higher order loop protocol
|
||||
|
||||
goof supports a higher order looping protocol, based on srfi-158 generators:
|
||||
|
||||
(loop ((:for food (in-list '(banana cake grape cake bean cake)))
|
||||
(:for true? (in-cycle (in-list '(#t #f)))))
|
||||
(display "The ")
|
||||
(display food)
|
||||
(display " is a ")
|
||||
(if true?
|
||||
(display food)
|
||||
(display "LIE!"))
|
||||
(newline))
|
||||
|
||||
In the above example true? never ends, but restarts every time the list is exhausted.
|
||||
|
||||
### Regressions compared to foof-loop
|
||||
|
||||
only accumulating clauses are visible in the final-expression. This is due to sequence clauses not being promoted through to outer loops (since they should not keep their state if an inner loop is exited).
|
||||
|
||||
Due to clause reordering, positional updates are not supported. If you want to update your loop vars, do so using named update (see below).
|
||||
|
||||
### changes
|
||||
|
||||
(with var [init [step [guard]]]) => (:for var (in init [step [stop-expr]])).
|
||||
|
||||
guard was a procedure, but now it is an expression.
|
||||
|
||||
(with var 10 (- var 1) negative?) => (:for var (in 10 (- var 10) (negative? var)))
|
||||
|
||||
## Features
|
||||
|
||||
### Lexical order of clauses
|
||||
|
@ -87,6 +41,18 @@ Due to clause reordering, positional updates are not supported. If you want to u
|
|||
=> acc)
|
||||
```
|
||||
|
||||
There is one caveat to this: some accumulating clauses (currently only vectoring with :length specified) have an implicit :break clause. This is tested AFTER the accumulation takes place. So: if the last position of the vector is set, the loop halts.
|
||||
|
||||
``` scheme
|
||||
(loop ((:for a (in-list '(1 2 3)))
|
||||
(:acc vec (vectoring a (:length 2)))
|
||||
;; implicit :break (= vec-index 2)
|
||||
(:acc sum (summing a)))
|
||||
=> (values vec sum))
|
||||
;; => #(1 2) 1
|
||||
|
||||
```
|
||||
|
||||
### Loop naming to make it "fold right"
|
||||
|
||||
You can of course still have a larger control of when to loop by naming your loop:
|
||||
|
@ -131,6 +97,26 @@ The iterator protocol allows exposing the loop variables
|
|||
|
||||
;; => (1 : 2 : 3)
|
||||
```
|
||||
In the above example, pair is bound to the pair where elt is the car.
|
||||
|
||||
### Higher order loop protocol
|
||||
|
||||
goof supports a higher order looping protocol, based on srfi-158 generators:
|
||||
|
||||
``` scheme
|
||||
|
||||
(loop ((:for food (in-list '(banana cake grape cake bean cake)))
|
||||
(:for true? (in-cycle (in-list '(#t #f)))))
|
||||
(display "The ")
|
||||
(display food)
|
||||
(display " is a ")
|
||||
(if true?
|
||||
(display food)
|
||||
(display "LIE!"))
|
||||
(newline))
|
||||
```
|
||||
|
||||
In the above example true? never ends, but restarts every time the list is exhausted.
|
||||
|
||||
### :final is context sensitive (compared to Racket's #:final)
|
||||
|
||||
|
@ -145,9 +131,9 @@ The iterator protocol allows exposing the loop variables
|
|||
;; => ((1 . a) (1 . b) (2 . a) (2 . b))
|
||||
```
|
||||
|
||||
The racket counterpart would result in ((1 . a) (1 . b) (2 . a))
|
||||
The racket counterpart would result in ((1 . a) (1 . b) (2 . a)). This comes at :final clauses being less efficient than racket's #:final, but not by much.
|
||||
|
||||
### for-clauses can refer to eachother
|
||||
### :for-clauses can refer to eachother
|
||||
|
||||
The iterative fibonacci loop is weird to write using for/fold. goof fixes this:
|
||||
``` scheme
|
||||
|
@ -171,7 +157,18 @@ The iterative fibonacci loop is weird to write using for/fold. goof fixes this:
|
|||
;; => 6 (1 2 2 3 3 4)
|
||||
```
|
||||
|
||||
### Pattern matching
|
||||
|
||||
For clauses which bind "body bindings" (every one except (in ...)) can use pattern matching based on Alex Shinn's excellent match.scm.
|
||||
|
||||
``` scheme
|
||||
(loop ((:for (key . val) (in-list '((a . 1) (b . 2) c . 3)))
|
||||
(:acc sum (summing val)))
|
||||
=> sum)
|
||||
;; => 6
|
||||
|
||||
This also works with :bind clauses.
|
||||
```
|
||||
|
||||
### Simple forms
|
||||
I also provide simplified forms for many common operations. Omitting :for is allowed, and :acc clauses are not allowed.
|
||||
|
@ -245,7 +242,7 @@ $5 = (let loopy-loop ((cursor (read)))
|
|||
(let ((a (car cursor)) (succ (cdr cursor)))
|
||||
(if (even? a)
|
||||
(cons a (loopy-loop succ))
|
||||
(loopy-loop
|
||||
(loopy-loop)))))
|
||||
|
||||
|
||||
;; The code expansion of the partition procedure above produces
|
||||
|
@ -265,15 +262,23 @@ $5 = (let loopy-loop ((cursor (read)))
|
|||
|
||||
```
|
||||
|
||||
|
||||
## Differences from foof-loop
|
||||
|
||||
This used to be a pretty vast collection of examples. goof-loof is now different enough from foof loop that you can't expect to carry your foof-loop skills over to goof-loop. There are however two notable regressions.
|
||||
|
||||
### Regressions compared to foof-loop
|
||||
only accumulating clauses are visible in the final-expression. This is due to sequence clauses not being promoted through to outer loops (since they should not keep their state if an inner loop is exited).
|
||||
|
||||
Due to clause reordering, positional updates are not supported. If you want to update your loop vars, do so using named update (see below).
|
||||
|
||||
## Todo
|
||||
Tests!
|
||||
|
||||
Finish documentation.
|
||||
|
||||
## foof, what a guy
|
||||
|
||||
I have previously expressed some admiration for Alex and I will do it again. The source of chibi loop is extremely elegant, and all but the hairiest part is written in syntax-rules. Not only has he written my two favourite SRFIs, his input in all the other discussions I have seen is always on-point, pragmatic and generally fantastic. He neither knows of this project, nor embraces it in any way. Y'all should go look at the source of (chibi loop) though.
|
||||
I have previously expressed some admiration for Alex Shinn and I will do it again. The source of chibi loop is extremely elegant, and all but the hairiest part is written in syntax-rules. Not only has he written my two favourite SRFIs, his input in all the other discussions I have seen is always on-point, pragmatic and generally fantastic. He neither knows of this project, nor embraces it in any way. Y'all should go look at the source of (chibi loop) though.
|
||||
|
||||
## Licence
|
||||
|
||||
The same BSD-styled license Alex uses for chibi-loop.
|
||||
goof started off by copying the iterator protocol of (chibi loop), and things sort of went downhill from there. Despite this, there is still quite a lot of code (especially in iterators.scm) that I didn't write myself. I only made it ugly. Thus goof is licensed under the same BSD-styled license Alex uses for chibi-loop.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue