efficient looping facility for guile
580d3fb365ea — Linus Björnstam default tip 4 years ago
Added hash for-loops
9678709999c7 — Linus Björnstam 4 years ago
Added tag v1.0 for changeset 4f0a61a2f248
4f0a61a2f248 — Linus Björnstam v1.0 4 years ago
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))

(erathostenes 10) 
;; => (2 3 5 7)

This all produces efficient code. The sieve above becomes:

(define (erathostenes n)
  (let ((vec (make-vector n #t)))
     (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)
                                         (vector-set! vec container239 #f)
                                         (loop (+ container239
                (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))
      ((>= 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.

#Going forward?

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.