diff --git a/README.md b/README.md index e11aa5b..7c77338 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,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)) - (:acc acc (summing b))) - => acc) + (:acc acc (summing b)))) ;; => 21 ``` @@ -37,8 +36,7 @@ It is written in a weird markdown/xml chimaera. You can find it in documentation (:when (test? b)) (:bind (c (expensive-operation2 b))) (:when test2? c) - (:acc acc (listing c)))) - => acc) + (:acc acc (listing c))))) ``` 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. @@ -47,10 +45,22 @@ There is one caveat to this: some accumulating clauses (currently only vectoring (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)) + (:acc sum (summing a)))) ;; => #(1 2) 1 +``` +### Ability to be explicit about returned values +If you have no "final expression", denoted by => expr, one is added that returns all final values of the :acc clauses in the loop. If none is present, the return value is unspecified. + +``` scheme +(loop ((:for a (up-from 1 5)) + (:acc sum (summing a)))) +;; => 10 + +(loop ((:for a (up-from 1 5)) + (:acc sum (summing a))) + => (- sum)) +;; => -10 ``` ### Loop naming to make it "fold right" @@ -66,6 +76,8 @@ You can of course still have a larger control of when to loop by naming your loo ;; => (-1 4 -9 16 -25 36 -49 64 -81 100) ``` +Replace that cons with stream-cons and you have a lazy construct. + ### Named updates ``` scheme @@ -81,7 +93,7 @@ You can of course still have a larger control of when to loop by naming your loo (continue (=> unsatisfied (cons element unsatisfied)))))) (partition '(1 2 3 4 5) odd?) -;; => (values (1 3 5) (2 4)) +;; => (1 3 5) (2 4) ``` ### Exposing loop variables @@ -123,15 +135,14 @@ In the above example true? never ends, but restarts every time the list is exhau ``` scheme (loop ((:for elt (in-list '( 1 2 3))) - :final (= elt 2) + (:final (= elt 2)) (:for ab (in-list '(a b))) - (:acc acc (listing (cons elt ab))) - => acc)) + (:acc acc (listing (cons elt ab)))) ;; => ((1 . a) (1 . b) (2 . a) (2 . b)) ``` -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. +The racket counterpart would result in ((1 . a) (1 . b) (2 . a)). This comes at :final clauses being marginally less efficient than racket's #:final. ### :for-clauses can refer to eachother @@ -139,9 +150,8 @@ The iterative fibonacci loop is weird to write using for/fold. goof fixes this: ``` scheme (loop ((:for a (in 0 b)) (:for b (in 1 (+ a b))) - (:for count (up-from 0 (to 100))) + (:for count (up-from 0 (:to 100))) (:acc acc (listing b))) - => acc (display b) (newline)) ``` ### Accumulators and arbitrary code can be placed in subloops @@ -152,8 +162,7 @@ The iterative fibonacci loop is weird to write using for/fold. goof fixes this: (:do (display "Entering subloop!") (newline)) :subloop (:for b (up-from a (:to (+ a 2)))) - (:acc ab (listing b))) - => (values aa ab)) + (:acc ab (listing b)))) ;; => 6 (1 2 2 3 3 4) ``` @@ -163,8 +172,7 @@ For clauses which bind "body bindings" (every one except (in ...)) can use patte ``` scheme (loop ((:for (key . val) (in-list '((a . 1) (b . 2) c . 3))) - (:acc sum (summing val))) - => sum) + (:acc sum (summing val)))) ;; => 6 This also works with :bind clauses. @@ -183,12 +191,12 @@ I also provide simplified forms for many common operations. Omitting :for is all (loop/product ((a (in-list '(2 3 4)))) a) -;; => 24 +;; => 24 (loop/first ((a (in-list '(a b c 3 4 d))) (:when (integer? a))) (display a) a) -;; => displays 3 and returns 3. +;; => displays 3 and returns 3. (loop/last ((a (in-list '(a b c d e f))) (:break (eq? a 'e))) a) @@ -215,8 +223,7 @@ Speed is good. Despite the rather involved expansion you can see in the document ``` scheme > ,opt (loop ((:for a (in-list '(1 2 3 4))) (:when (even? a)) - (:acc acc (listing a))) - => acc) + (:acc acc (listing a)))) $1 = (let loopy-loop ((cursor-1 '()) (cursor '(1 2 3 4))) (if (pair? cursor) (let ((a (car cursor)) (succ (cdr cursor))) diff --git a/goof-impl.scm b/goof-impl.scm index 50abfc6..9197cad 100644 --- a/goof-impl.scm +++ b/goof-impl.scm @@ -56,14 +56,10 @@ (define-syntax %loop - (syntax-rules (=>) - ((%loop o () => expr body ...) - (%loop o ((:for ensure-once (up-from 0 1))) => expr body ...)) + (syntax-rules () ((%loop o () body ...) (%loop o ((:for ensure-once (up-from 0 1))) body ...)) - ((%loop o name () => expr body ...) - (%loop o name ((:for ensure-once (up-from 0 1))) => expr body ...)) - ((%loop o name () body ...) + ((%loop o name () body ...) (%loop o name ((:for ensure-once (up-from 0 1))) body ...)) ((%loop o (clauses ...) body ...) (ensure-for-clause #f () (clauses ...) o @@ -125,8 +121,12 @@ (syntax-rules (=> :for :acc :when :unless :break :final :do :bind :subloop) ((_ orig name l a v c r f ff user () => expr . body) (emit orig name l a v c r f ff user expr . body)) - ((_ orig name l a v c r f ff user () . body) - (emit orig name l a v c r f ff user (if #f #f) . body)) + ((_ orig name l a v c r () ff user () . body) + (emit orig name l a v c r () ff user (if #f #f) . body)) + + ;; If we have no final-expr, but we have final bindings, we return those. + ((_ orig name l a v c r ((final-binding expr) ...) ff user () . body) + (emit orig name l a v c r ((final-binding expr) ...) ff user (values final-binding ...) . body)) ;; user bindings ((_ orig name l a v c r f ff ((cur-ul ...) . ul-rest) ((:bind (id id* ... expr) ...) clauses ...) . body)