Fixed named update and a bug in accumulating
* goof.scm: Removed positional updates and fixed named updates * iterators.scm: Fixed bug in accumulating where only lists were supported.
This commit is contained in:
parent
b3efccb4aa
commit
f2496604d5
3 changed files with 55 additions and 61 deletions
36
README.md
36
README.md
|
@ -1,14 +1,14 @@
|
|||
# goof-loop - a scheme looping facility
|
||||
|
||||
WARNING: CURRENTLY PRE-ALPHA. The examples in this document are not consistent with the current direction I am pushing this (even though they _should_ work.
|
||||
WARNING: CURRENTLY PRE-ALPHA. The examples in this document are not consistent with the current direction I am pushing this (even though they _should_ work).
|
||||
|
||||
goof-loops aims to be an amalgamation of the racket for loops and Alex Shinn's foof-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. This is understandable given how they are tied to the underlying racket sequences, but still somewhat disappointing. 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:
|
||||
|
||||
```
|
||||
(loop ((:for a (in 0 b))
|
||||
(:for b (in 1 (+ a b)))
|
||||
(:for count (up-from 0 (to 1000)))
|
||||
(:for acc (listing b)))
|
||||
(:acc acc (listing b)))
|
||||
=> acc
|
||||
(display b) (newline))
|
||||
```
|
||||
|
@ -22,7 +22,7 @@ Compared to foof-loop, some things are added. Apart from minor syntactic changes
|
|||
(loop ((:for a (in-list lst))
|
||||
(:when (pair? a))
|
||||
(:for b (in-list a))
|
||||
(:for acc (summing b)))
|
||||
(:acc acc (summing b)))
|
||||
=> acc)
|
||||
```
|
||||
|
||||
|
@ -54,7 +54,7 @@ only :acc clauses are visible in the final-expression. This is due to for-clause
|
|||
|
||||
(with var 10 (- var 1) negative?) => (:for var (in 10 (- var 10) (negative? var)))
|
||||
|
||||
I plan to remove non-named variable updates. That is a minor inconveniance, but unnamed updates has been my largest source of bugs, so I have grown to hate them.
|
||||
Positional updates of variables is not supported, due to goof-loop reordering the loop-vars - which there are reasons for.
|
||||
|
||||
### similarities
|
||||
|
||||
|
@ -70,18 +70,22 @@ You can of course still have a larger control of your loops:
|
|||
;; => (-1 4 -9 16 -25 36 -49 64 -81 100)
|
||||
```
|
||||
|
||||
Named updates have a bug, sadly, but works if there is only _one_ instance of the iteration macro. This doesn't curretnly work, but will in a little time:
|
||||
Named updates also work.
|
||||
|
||||
```
|
||||
;; Shamelessly stolen from Taylor Campbell's foof-loop documentation
|
||||
(loop continue ((:for element (in-list list))
|
||||
(:acc satisfied (in '()))
|
||||
(:acc unsatisfied (in '())))
|
||||
=> (values (reverse satisfied)
|
||||
(reverse unsatisfied))
|
||||
(if (predicate element)
|
||||
(continue (=> satisfied (cons element satisfied)))
|
||||
(continue (=> unsatisfied (cons element unsatisfied))))))
|
||||
(define (partition list predicate)
|
||||
(loop continue ((:for element (in-list list))
|
||||
(:acc satisfied (in '()))
|
||||
(:acc unsatisfied (in '())))
|
||||
=> (values (reverse satisfied)
|
||||
(reverse unsatisfied))
|
||||
(if (predicate element)
|
||||
(continue (=> satisfied (cons element satisfied)))
|
||||
(continue (=> unsatisfied (cons element unsatisfied))))))
|
||||
|
||||
(partition '(1 2 3 4 5) odd?)
|
||||
;; => (values (1 3 5) (2 4))
|
||||
```
|
||||
|
||||
|
||||
|
@ -95,7 +99,9 @@ Should we add finalizers for :for-clauses? I can't see the need outside of a pot
|
|||
|
||||
Is (:for var (in init step stop)) and (:acc var (in init update)) good syntax? the :with clause of foof-loop is nice, but what should it be called for accumulators? Should we go back to calling both :acc and :for just ":for" and re-add :with and an accumulating counterpart? What should that accumulating counterpart be called? :acc?
|
||||
|
||||
Add racket #:final clauses.
|
||||
Add racket #:final clauses.
|
||||
|
||||
Add simple versions of loop. loop/list, loop/sum, loop/last, loop/first, and so on.
|
||||
|
||||
## foof, what a guy
|
||||
|
||||
|
|
76
goof.scm
76
goof.scm
|
@ -37,12 +37,16 @@
|
|||
;; * Adds :when, :unless, and :break clauses that controls when the loop
|
||||
;; body executes and when values are collected by accumulating for clauses.
|
||||
;; similar to how #:when, #:unless and #:break works in racket.
|
||||
;; * Planned: add support for subloops, akin to what the starified loops of
|
||||
;; racket do.
|
||||
;;
|
||||
;; It restricts chibi loops in the following ways:
|
||||
;; * with- and for-clauses are no longer visible in the final expression, for that you
|
||||
;; must use a clause for which I don't have a name yet.
|
||||
;; must use an accumulator clause.
|
||||
;; * Positional update is not supported. It seems error-prone once you start
|
||||
;; having a lot of loop variables, and because goof-loop does some re-ordering
|
||||
;; that foof loop does not. For example:
|
||||
;; (:for a (in 0 (+ a 1))) (:acc acc (in '() (cons a acc)))
|
||||
;; are actually reordered in a loop, because accumulators and for loops are separated
|
||||
;; due to having to propagate the accumulators through the loop.
|
||||
|
||||
|
||||
(use-modules (helpers)
|
||||
|
@ -51,15 +55,16 @@
|
|||
(include "iterators.scm")
|
||||
|
||||
;; TODO: Add intermediate subloops. Make sure that accumulators are properly propagated.
|
||||
;; TODO. fix let-kw-form. Don't use mutation. This should work:(define (partition predicate list)
|
||||
;; (loop continue ((:for element (in-list list))
|
||||
;; (:acc satisfied (in '()))
|
||||
;; (:acc unsatisfied (in '())))
|
||||
;; => (values (reverse satisfied)
|
||||
;; (reverse unsatisfied))
|
||||
;; (if (predicate element)
|
||||
;; (continue (=> satisfied (cons element satisfied)))
|
||||
;; (continue (=> unsatisfied (cons element unsatisfied))))))
|
||||
;; DONE: fix let-kw-form. Don't use mutation. This should be tested:
|
||||
;; (define (partition predicate list)
|
||||
;; (loop continue ((:for element (in-list list))
|
||||
;; (:acc satisfied (in '()))
|
||||
;; (:acc unsatisfied (in '())))
|
||||
;; => (values (reverse satisfied)
|
||||
;; (reverse unsatisfied))
|
||||
;; (if (predicate element)
|
||||
;; (continue (=> satisfied (cons element satisfied)))
|
||||
;; (continue (=> unsatisfied (cons element unsatisfied))))))
|
||||
|
||||
|
||||
|
||||
|
@ -130,7 +135,10 @@
|
|||
((_ orig name l a v c r f ul uw ub ((:acc var (in init update)) clauses ...) . body)
|
||||
(cl-next () ((var init update)) () () () ((var var)) orig name l a v c r f ul uw ub (clauses ...) . body))
|
||||
((_ orig name l a v c r f ul uw ub ((:acc var (in init)) clauses ...) . body)
|
||||
(cl-next () ((var init var)) () () () ((var var)) orig name l a v c r f ul uw ub (clauses ...) . body))
|
||||
(cl-next () ((var init var)) () () () ((var var)) orig name l a v c r f ul uw ub (clauses ...) . body))
|
||||
;; Accumulator clause with a proper accumulator.
|
||||
((_ orig name l a v c r f ul uw ub ((:acc id ids ... (iterator source ...)) clauses ...) . body)
|
||||
(iterator ((id ids ...) (source ...)) cl-next orig name l a v c r f ul uw ub (clauses ...) . body))
|
||||
|
||||
;; user-whens
|
||||
((_ orig name l a v c r f ul ((cur-uw ...) . uw-rest) ub ((:when test) clauses ...) . body)
|
||||
|
@ -366,26 +374,13 @@
|
|||
(define (syntax= s1 s2)
|
||||
(equal? (syntax->datum s1) (syntax->datum s2)))
|
||||
|
||||
(define (named-update? syn)
|
||||
(syntax-case syn (=>)
|
||||
((=> var update) #t)
|
||||
(_ #f)))
|
||||
|
||||
|
||||
(define (update-psn! params psn val)
|
||||
(list-set! params psn
|
||||
(list (car (list-ref params psn)) val)))
|
||||
|
||||
(define (update-name! params name val)
|
||||
(let loop ((params params))
|
||||
(cond
|
||||
((null? params) (error "unknown loop parameter name " name (list '=> name val)))
|
||||
((syntax= name (caar params))
|
||||
(set-cdr! (car params) (list val))
|
||||
(display (syntax->datum val))
|
||||
)
|
||||
(else
|
||||
(loop (cdr params))))))
|
||||
(define (update-name params name val)
|
||||
(cond
|
||||
((null? params) (error "unknown loop parameter name " name (list '=> name val)))
|
||||
((syntax= name (caar params))
|
||||
(cons (list (caar params) val) (cdr params)))
|
||||
(else
|
||||
(cons (car params) (update-name (cdr params) name val)))))
|
||||
|
||||
(define (syntax->list stx)
|
||||
(syntax-case stx ()
|
||||
|
@ -396,23 +391,16 @@
|
|||
((_ macro-name (loop-name (var step) ...) . body)
|
||||
(let-syntax ((macro-name
|
||||
(lambda (stx)
|
||||
;; this way of formulating params means it is an alist with syntax objects
|
||||
;; as keys instead of a list of syntax objects
|
||||
(define params (list #'(var step) ...))
|
||||
(with-ellipsis :::
|
||||
(let loop ((lst (cdr (syntax->list stx))) (pos 0))
|
||||
(let loop ((lst (cdr (syntax->list stx)))
|
||||
(params (list #'(var step) ...)))
|
||||
(if (null? lst)
|
||||
(with-syntax ((((v s) :::) params))
|
||||
#'(loop-name s :::))
|
||||
(syntax-case (car lst) (=>)
|
||||
((=> name val)
|
||||
(update-name! params #'name #'val)
|
||||
(loop (cdr lst) #f))
|
||||
(val pos
|
||||
(begin
|
||||
(update-psn! params psn #'val)
|
||||
(loop (cdr lst) (+ pos 1))))
|
||||
(_ (error "Positional arguments cannot be updated after a named argument")))))))))
|
||||
(loop (cdr lst) (update-name params #'name #'val)))
|
||||
(_ (error "Malformed looping clause in macro")))))))))
|
||||
. body))))
|
||||
|
||||
|
||||
|
|
|
@ -268,7 +268,7 @@
|
|||
(accumulating (kons final i) ((var cursor) x) n . rest))
|
||||
((accumulating (kons final init) ((var cursor) (expr (if check))) n . rest)
|
||||
(n ((tmp-kons kons))
|
||||
((cursor '() (if check (tmp-kons expr cursor) cursor)))
|
||||
((cursor init (if check (tmp-kons expr cursor) cursor)))
|
||||
()
|
||||
()
|
||||
()
|
||||
|
@ -276,7 +276,7 @@
|
|||
. rest))
|
||||
((accumulating (kons final init) ((var cursor) (expr)) n . rest)
|
||||
(n ((tmp-kons kons))
|
||||
((cursor '() (tmp-kons expr cursor)))
|
||||
((cursor init (tmp-kons expr cursor)))
|
||||
()
|
||||
()
|
||||
()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue