diff --git a/documentation/doc.html b/documentation/doc.html index aa3f7b3..ee926ea 100644 --- a/documentation/doc.html +++ b/documentation/doc.html @@ -67,7 +67,7 @@
 (loop ((:for a (in-list '(1 2 3))) 
         :subloop
-        (:for b (up-from 0 (to a)))
+        (:for b (up-from 0 (:to a)))
         (:acc acc (listing (cons a b)))))
   
 ;; => ((1 . 0) (2 . 0) (2 . 1) (3 . 0) (3 . 1) (3 . 2))
@@ -75,7 +75,7 @@
 
 (loop ((:for a (in-list '(1 2 3))) 
        (:break (= 3 a))
-       (:for b (up-from 0 (to a)))
+       (:for b (up-from 0 (:to a)))
        (:acc acc (listing (cons a b)))))
   
 ;; => ((1 . 0) (2 . 0) (2 . 1))
@@ -83,10 +83,10 @@
 
 (loop ((:for a (in-list '(1 2 3 4)))
        (:final (= 3 a))
-       (:for b (up-from 0 (to a)))
+       (:for b (up-from 0 (:to a)))
        (:acc acc (listing (cons a b)))))
   
-;; => ((1 . 0) (2 . 0) (2 . 1) (3 . 0) (3 . 1))
+;; => ((1 . 0) (2 . 0) (2 . 1) (3 . 0) (3 . 1) (3 . 2))
       

The :final clause is actually equivalent to something alike the following:

 (loop ((:for a (in-list '(1 2 3 4)))
@@ -117,13 +117,13 @@
 

Some loop clauses have the option of exposing their loop variable(s). The option to do so is documented under the documentation for each clause.

Simple forms

The pure loop macro is quite a big hammer for most tasks. Often we want to do simple things, like collect elements into a list or a vector, which means the extra housekeeping of separating accumulators and for clauses are too much heavy lifting. goof-loop provides several simpler forms that can be used in those cases. In these simpler forms :acc is disallowed, and everything not identified as anything else is assumed to be a :for clause. The loop below accumulates the 100 first fibonacci numbers into a list.

-(loop/list ((count (up-from 0 (to 100)))
+(loop/list ((count (up-from 0 (:to 100)))
             (a (in 0 b))
             (b (in 1 (+ a b))))
   b)
       

The simple forms provided by goof-loop are the following:

-
Scheme syntax: loop/first
(loop/first (clauses ...) body ...)

If any body is ever evaluated, stop and return the value of that evaluation. If no body is ever evaluated the return value is unspecified.

-
Scheme syntax: loop/last
(loop/last (clauses ...) body ...)

Returns the result of the last body to be evaluated. If no body is evaluated the return value is unspecified.

+
Scheme syntax: loop/first
(loop/first [:default #f] (clauses ...) body ...)

If any body is ever evaluated, stop and return the value of that evaluation. If no body is evaluated it returns the value specified by :default, which defaults to #f.

+
Scheme syntax: loop/last
(loop/last [:default #f] (clauses ...) body ...)

Returns the result of the last body to be evaluated. If no body is evaluated it returns the value specified by :default, which defaults to #f.

Scheme syntax: loop/list
(loop/list (clauses ...) body ...)

Iterates over clauses and builds a list of the result of every evaluation of body. The order of the list is the same as the order body was evaluated in. The result of the first evaluation of body is the first element of the resulting list.

The list returned is the same even when used with multi-shot continuations.

If no body is evaluated, the result is the empty list.

@@ -167,25 +167,25 @@

:acc-clauses

Accumulating clauses differ from :for-clauses in 2 significant ways. They have a final value available in the final-expr, 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.

Another small thing is that for some :acc-clauses, the binding may sometimes only be visible to the user in the final-expr, but like :for-clauses they sometimes offer the programmer to name the loop variables.

Many accumulating clauses support an if form. If such a clause is given, accumulation will only happen if the guard clause returns true.

-
Scheme syntax: listing
(:acc binding (listing [(initial init)] expr [if guard]))

Accumulates expr into a list. ´bindingis only accesible in the final-expression. The list is in the same order as the loop bodies were evaluated. Ifinitialis given that will be used as the tail of the accumulated results. It defaults to’()`.

-
Scheme syntax: listing-reverse
(:acc binding (listing-reverse [(initial init)] expr [if guard]))

The same as listing but the resulting list in in reverse order. If the order of the resulting list does not matter, this will be faster than the regular listing as it will not preform any reverse at the end.

-
Scheme syntax: appending
(:acc binding (appending [(initial init)] expr [if guard]))

expr evaluates to a list that is then appended to the accumulated result.

+
Scheme syntax: listing
(:acc binding (listing [(:initial init)] expr [if guard]))

Accumulates expr into a list. ´bindingis only accesible in the final-expression. The list is in the same order as the loop bodies were evaluated. Ifinitialis given that will be used as the tail of the accumulated results. It defaults to’()`.

+
Scheme syntax: listing-reverse
(:acc binding (listing-reverse [(:initial init)] expr [if guard]))

The same as listing but the resulting list in in reverse order. If the order of the resulting list does not matter, this will be faster than the regular listing as it will not preform any reverse at the end.

+
Scheme syntax: appending
(:acc binding (appending [(:initial init)] expr [if guard]))

expr evaluates to a list that is then appended to the accumulated result.

 (loop ((:for elt (in-list '((1 2) (3 4))))
-       (:acc acc (appending (initial '(0)) elt)))
+       (:acc acc (appending (:initial '(0)) elt)))
   => acc)
   ;; => (0 1 2 3 4)     
-        
Scheme syntax: appending-reverse
(:acc binding (appending-reverse [(initial init)] expr [if guard]))

expr evaluates to a list that is then consed element by element onto the already accumulated results. The default initial value is '().

+
Scheme syntax: appending-reverse
(:acc binding (appending-reverse [(:initial init)] expr [if guard]))

expr evaluates to a list that is then consed element by element onto the already accumulated results. The default initial value is '().

 (loop ((:for elt (in-list '((1 2) (3 4))))
-       (:acc acc (appending-reverse (initial '(0)) elt)))
+       (:acc acc (appending-reverse (:initial '(0)) elt)))
   => acc)
   ;; => (4 3 2 1 0)
-        
Scheme syntax: summing
(:acc binding (summing [(initial init)] expr [(if guard)]))

Adds the result of expr together using +. The default initial value is 0.

-
Scheme syntax: multiplying
(:acc binding (multiplying [(initial init)] expr [(if guard)]))

Multiplies the result of expr using *. The default initial value is 1.

-
Scheme syntax: hashing
(:acc binding (hashing [(initial init)] key value [(if guard)]))

Adds the mapping (key => value) to the hashtable binding using equal?-hashing. The initial hash table is an empty hash-table. binding is bound to the hash table throughout the loop, and its content can be mutated in the loop body.

-
Scheme syntax: hashving
(:acc binding (hashving [(initial init)] key value [(if guard)]))

Adds the mapping (key => value) to the hashtable binding using eqv?-hashing. The initial hash table is an empty hash-table. binding is bound to the hash table throughout the loop, and its content can be mutated in the loop body.

-
Scheme syntax: hashqing
(:acc binding (hashqing [(initial init)] key value [(if guard)]))

Adds the mapping (key => value) to a hashtable using eq?-hashing. The initial hash table is an empty hash-table.binding is bound to the hash table throughout the loop, and its can be mutated in the loop body.

+
Scheme syntax: summing
(:acc binding (summing [(:initial init)] expr [(if guard)]))

Adds the result of expr together using +. The default initial value is 0.

+
Scheme syntax: multiplying
(:acc binding (multiplying [(:initial init)] expr [(if guard)]))

Multiplies the result of expr using *. The default initial value is 1.

+
Scheme syntax: hashing
(:acc binding (hashing [(:initial init)] key value [(if guard)]))

Adds the mapping (key => value) to the hashtable binding using equal?-hashing. The initial hash table is an empty hash-table. binding is bound to the hash table throughout the loop, and its content can be mutated in the loop body.

+
Scheme syntax: hashving
(:acc binding (hashving [(:initial init)] key value [(if guard)]))

Adds the mapping (key => value) to the hashtable binding using eqv?-hashing. The initial hash table is an empty hash-table. binding is bound to the hash table throughout the loop, and its content can be mutated in the loop body.

+
Scheme syntax: hashqing
(:acc binding (hashqing [(:initial init)] key value [(if guard)]))

Adds the mapping (key => value) to a hashtable using eq?-hashing. The initial hash table is an empty hash-table.binding is bound to the hash table throughout the loop, and its can be mutated in the loop body.

Scheme syntax: vectoring
(:acc binding [index] (vectoring expr [(:length len) [(:fill fill)]]))

Accumulates the result of expr into a vector. If len and fill is given the vector will be at most len elements long and any unfilled indexes will contain the element fill. The loop will exit when len elements have been accumulated.

If length is not given, the vector will be expanded as required.

A vectoring clause adds an implicit (:break (= index len)) after the vectoring clause. Once the last element of the vector is filled, the loop will stop and no subsequent clauses or body will be executed.

diff --git a/documentation/doc.xml b/documentation/doc.xml index 8751065..5545149 100644 --- a/documentation/doc.xml +++ b/documentation/doc.xml @@ -106,7 +106,7 @@ (loop ((:for a (in-list '(1 2 3))) :subloop - (:for b (up-from 0 (to a))) + (:for b (up-from 0 (:to a))) (:acc acc (listing (cons a b))))) ;; => ((1 . 0) (2 . 0) (2 . 1) (3 . 0) (3 . 1) (3 . 2)) @@ -117,7 +117,7 @@ (loop ((:for a (in-list '(1 2 3))) (:break (= 3 a)) - (:for b (up-from 0 (to a))) + (:for b (up-from 0 (:to a))) (:acc acc (listing (cons a b))))) ;; => ((1 . 0) (2 . 0) (2 . 1)) @@ -128,10 +128,10 @@ (loop ((:for a (in-list '(1 2 3 4))) (:final (= 3 a)) - (:for b (up-from 0 (to a))) + (:for b (up-from 0 (:to a))) (:acc acc (listing (cons a b))))) - ;; => ((1 . 0) (2 . 0) (2 . 1) (3 . 0) (3 . 1)) + ;; => ((1 . 0) (2 . 0) (2 . 1) (3 . 0) (3 . 1) (3 . 2)) The :final clause is actually equivalent to something alike the following: @@ -182,7 +182,7 @@ The pure `loop` macro is quite a big hammer for most tasks. Often we want to do simple things, like collect elements into a list or a vector, which means the extra housekeeping of separating accumulators and for clauses are too much heavy lifting. goof-loop provides several simpler forms that can be used in those cases. In these simpler forms :acc is disallowed, and everything not identified as anything else is assumed to be a :for clause. The loop below accumulates the 100 first fibonacci numbers into a list. - (loop/list ((count (up-from 0 (to 100))) + (loop/list ((count (up-from 0 (:to 100))) (a (in 0 b)) (b (in 1 (+ a b)))) b) @@ -192,15 +192,15 @@ -
(loop/first (clauses ...) body ...)
+
(loop/first [:default #f] (clauses ...) body ...)
- If any body is ever evaluated, stop and return the value of that evaluation. If no body is ever evaluated the return value is unspecified. + If any body is ever evaluated, stop and return the value of that evaluation. If no body is evaluated it returns the value specified by `:default`, which defaults to #f.
-
(loop/last (clauses ...) body ...)
+
(loop/last [:default #f] (clauses ...) body ...)
- Returns the result of the last body to be evaluated. If no body is evaluated the return value is unspecified. + Returns the result of the last body to be evaluated. If no body is evaluated it returns the value specified by `:default`, which defaults to #f.
@@ -392,69 +392,69 @@ -
(:acc binding (listing [(initial init)] expr [if guard]))
+
(:acc binding (listing [(:initial init)] expr [if guard]))
Accumulates `expr` into a list. ´binding` is only accesible in the final-expression. The list is in the same order as the loop bodies were evaluated. If `initial` is given that will be used as the tail of the accumulated results. It defaults to `'()`.
-
(:acc binding (listing-reverse [(initial init)] expr [if guard]))
+
(:acc binding (listing-reverse [(:initial init)] expr [if guard]))
The same as `listing` but the resulting list in in reverse order. If the order of the resulting list does not matter, this will be faster than the regular listing as it will not preform any reverse at the end.
-
(:acc binding (appending [(initial init)] expr [if guard]))
+
(:acc binding (appending [(:initial init)] expr [if guard]))
`expr` evaluates to a list that is then appended to the accumulated result. (loop ((:for elt (in-list '((1 2) (3 4)))) - (:acc acc (appending (initial '(0)) elt))) + (:acc acc (appending (:initial '(0)) elt))) => acc) ;; => (0 1 2 3 4)
-
(:acc binding (appending-reverse [(initial init)] expr [if guard]))
+
(:acc binding (appending-reverse [(:initial init)] expr [if guard]))
`expr` evaluates to a list that is then consed element by element onto the already accumulated results. The default initial value is `'()`. (loop ((:for elt (in-list '((1 2) (3 4)))) - (:acc acc (appending-reverse (initial '(0)) elt))) + (:acc acc (appending-reverse (:initial '(0)) elt))) => acc) ;; => (4 3 2 1 0)
-
(:acc binding (summing [(initial init)] expr [(if guard)]))
+
(:acc binding (summing [(:initial init)] expr [(if guard)]))
Adds the result of `expr` together using `+`. The default initial value is 0.
-
(:acc binding (multiplying [(initial init)] expr [(if guard)]))
+
(:acc binding (multiplying [(:initial init)] expr [(if guard)]))
Multiplies the result of `expr` using `*`. The default initial value is 1.
-
(:acc binding (hashing [(initial init)] key value [(if guard)]))
+
(:acc binding (hashing [(:initial init)] key value [(if guard)]))
Adds the mapping `(key => value)` to the hashtable `binding` using equal?-hashing. The initial hash table is an empty hash-table. `binding` is bound to the hash table throughout the loop, and its content can be mutated in the loop body.
-
(:acc binding (hashving [(initial init)] key value [(if guard)]))
+
(:acc binding (hashving [(:initial init)] key value [(if guard)]))
Adds the mapping `(key => value)` to the hashtable `binding` using eqv?-hashing. The initial hash table is an empty hash-table. `binding` is bound to the hash table throughout the loop, and its content can be mutated in the loop body.
-
(:acc binding (hashqing [(initial init)] key value [(if guard)]))
+
(:acc binding (hashqing [(:initial init)] key value [(if guard)]))
Adds the mapping `(key => value)` to a hashtable using eq?-hashing. The initial hash table is an empty hash-table.`binding` is bound to the hash table throughout the loop, and its can be mutated in the loop body.
diff --git a/goof-impl.scm b/goof-impl.scm index 6e2c14b..fab2f1e 100644 --- a/goof-impl.scm +++ b/goof-impl.scm @@ -32,11 +32,15 @@ :when :unless :break :final :bind :do :subloop :for :acc ;; Auxiliary syntax for the iterators. :gen + ;; auxiliary syntax for some accumulators + :initial ;; auxiliary auxiliary syntax ;; for vectoring :length :fill ;;for up-from and down-to :to :by + ;; used by for/first and for/last + :default ;; Internal syntax. %acc is turned into :acc by the forify macro ;; it is used make it possible to report an error if :acc is used in ;; one of the simple macros. @@ -491,27 +495,32 @@ => acc (product-loop))))) -(define sentinel (list 'unique)) - -;; TODO: maybe have a look at the expansion of this. It seems weird. +;; This exploits that we give the loop a name, but don't add the loop to the end of the +;; body, thus returning whatever the last expr of body returns. (define-syntax loop/first - (syntax-rules () - ((n (clauses ...) body ...) + (syntax-rules (:default) + ((n :default val (clauses ...) body ...) (forify (n (clauses ...) body ...) loop/first - () (clauses ... (:final #t)) - => #f - body ...)))) - - -(define-syntax loop/last - (syntax-rules () + () (clauses ...) + => val + body ...)) ((n (clauses ...) body ...) + (loop/first :default #f (clauses ...) body ...)))) + + +;; unique value used for loop/last +(define sentinel (list 'unique)) +(define-syntax loop/last + (syntax-rules (:default) + ((n :default val (clauses ...) body ...) (forify (n (clauses ...) body ...) loop-name () (clauses ... (%acc acc (folding sentinel))) - => (if (eq? sentinel acc) #f acc) + => (if (eq? sentinel acc) val acc) (let ((result (let () body ...))) - (loop-name (=> acc result))))))) + (loop-name (=> acc result))))) + ((n (clauses ...) body ...) + (loop/last :default #f (clauses ...) body ...)))) (define-syntax loop/and (syntax-rules () diff --git a/goof.scm b/goof.scm index 021a0ac..fb7b49c 100644 --- a/goof.scm +++ b/goof.scm @@ -48,8 +48,10 @@ loop/list/parallel :when :unless :break :final :bind :subloop :do :for :acc + :initial :length :fill :to :by + :default in in-list diff --git a/goof/iterators.scm b/goof/iterators.scm index f332b28..d8030c5 100644 --- a/goof/iterators.scm +++ b/goof/iterators.scm @@ -373,10 +373,10 @@ (define-syntax accumulating - (syntax-rules (initial if :acc) + (syntax-rules (:initial if :acc) ((accumulating :acc (kons final init) ((var) . x) next . rest) (accumulating :acc (kons final init) ((var cursor) . x) next . rest)) - ((accumulating :acc (kons final init) ((var cursor) ((initial i) . x)) n . rest) + ((accumulating :acc (kons final init) ((var cursor) ((:initial i) . x)) n . rest) (accumulating :acc (kons final i) ((var cursor) x) n . rest)) ((accumulating :acc (kons final init) ((var cursor) (expr (if check))) n . rest) (n ((tmp-kons kons)) @@ -443,18 +443,18 @@ (syntax-rules () ((_ name default-make setter) (define-syntax name - (syntax-rules (:acc if initial) + (syntax-rules (:acc if :initial) ((_ :acc ((var) (key value)) n . rest) - (name :acc ((var) (key value (if #t) (initial default-make))) n . rest)) + (name :acc ((var) (key value (if #t) (:initial default-make))) n . rest)) ;; either init or if ((_ :acc ((var) (key value (if guard))) n . rest) - (name :acc ((var) (key value (if guard) (initial default-make))) n . rest)) + (name :acc ((var) (key value (if guard) (:initial default-make))) n . rest)) ((_ :acc ((var) (key value (initial init))) n . rest) - (name :acc ((var) (key value (if #t) (initial init))) n . rest)) + (name :acc ((var) (key value (if #t) (:initial init))) n . rest)) ;; both init and if - ((_ :acc ((var) (key value (initial init) (if guard))) n . rest) - (name ((var) (key value (if guard) (initial init))) n . rest)) - ((_ :acc ((var) (key value (if guard) (initial init))) n . rest) + ((_ :acc ((var) (key value (:initial init) (if guard))) n . rest) + (name ((var) (key value (if guard) (:initial init))) n . rest)) + ((_ :acc ((var) (key value (if guard) (:initial init))) n . rest) (n ((var init)) ((dummy (if #f #f) (if guard (setter var key value) (if #f #f)))) diff --git a/tests.scm b/tests.scm index d1366f3..5c03fb9 100644 --- a/tests.scm +++ b/tests.scm @@ -148,4 +148,9 @@ (loop/list ((a (up-from 1 (:by 2))) (b (in-list '(1 3 5)))) (+ a b)) '(2 6 10)) + +(test-equal "down-from-10" + (loop/sum ((a (down-from 11 (:to 1) (:by 2)))) + a) + 25) (test-end ":for-clauses")