Added hash for-loops
Added tag v1.0 for changeset 4f0a61a2f248
Really update readme. Mention stable versions and install instructions.
Licensed under the mozilla public license v2.
A re-implementation of a large-ish chunk of rackets for-macros. It is mostly compatible with racket's macros, with the largest omission being the body-or-break clause and some of the sequence iterators (like in-cycle, which can be covered by circular lists). There are other differences of course, like for/foldr not being on par feature-wise, and all the nicities you get by having a generic sequence interfce.
The macros are usable and produce optimal code in almost all situations. They have been written with a close eye to how the guile optimizer treats the output.
Documentation and installation instructions are found in docs.org or docs.html or in the wiki: https://man.sr.ht/~bjoli/for-loops-docs/for-loops.md. The markdown file has some oddities in it, but that is due to conversion errors going from org->markdown.
For a stable version download, look under "tags". Anything tagged with a version identifier is considered stable. For a global installation, unpack the .tar.gz and copy the "loops" folder to your site dir. To find the side-dir, execute the following at the command-line:
guile -c "(display (%site-dir))"
If you have a local load path, you can move the loops folder there. Import (loops for-loops) and enjoy!
(use-modules (loops for-loops))
(for/list ((ch (in-string "Hel2lo")) #:unless (char-numeric? ch))
(char-upcase ch))
;; => (#\H #\E #\L #\L #\O)
(for*/list ((a (in-range 1 3)) (b (in-range 1 3)))
(list a b))
;; => ((1 1) (1 2) (2 1) (2 2))
;; A rather naive sieve of erathostenes
(define (erathostenes n)
(define vec (make-vector n #t))
(for/list ([i (in-range 2 n)] #:when (vector-ref vec i))
(for ([j (in-range/incr (* 2 i) n i)])
(vector-set! vec j #f))
i))
(erathostenes 10)
;; => (2 3 5 7)
This all produces efficient code. The sieve above becomes:
(define (erathostenes n)
(let ((vec (make-vector n #t)))
(reverse!
(let loop ((acc '()) (container233 2))
(cond ((>= container233 n) acc)
((vector-ref vec container233)
(let ((acc (cons (begin
(let ((start243 (* 2 container233)))
(let loop ((container239 start243))
(if (>= container239 n)
(values)
(begin
(vector-set! vec container239 #f)
(loop (+ container239
container233))))))
container233)
acc)))
(loop acc (+ container233 1))))
(else (loop acc (+ container233 1))))))))
Cleaning the code up reveals code that a human would write:
(define (erathostenes n)
(define vec (make-vector n #t))
(let loop ((acc '()) (i 2))
(cond
((>= i n) (reverse! acc))
((vector-ref vec i)
(let loop ((j (* 2 i)))
(unless (>= j n)
(vector-set! vec j #f)
(loop (+ j i))))
(loop (cons i acc) (+ i 1)))
(else (loop acc (+ i 1))))))
Both code snippets run just as fast, and the simple optimization of just testing odd numbers can trivially be made to both examples.
I want to make these loops slightly more flexible, and allow for things like arbitrary transformations of values (like, say, loop in common lisp). Something like:
(for/last ((count (in-range 100))(a #:first 0 #:then b) (b #:first 1 #:then (+ a b)))
(display "FIBONACCI! ") (display b) (newline))
is not possible currently, and doing something similar would mean writing an ugly for/fold. This is a complex rewrite and will be some time into the future. If at all.