Defining a For Loop in Scheme

1.3k views Asked by At

Note: This appears to be Gauche Scheme version 0.9.3.3.
I cannot seem to wrap my head around these Lisp languages :/.

I'm trying to define a for loop syntax in Scheme. I'm not sure if this is doable with a recursive function (I think it is), but at this point I really want to get define-syntax working.

I can get the loop to run once (and then it looks like it terminates) with this code:

(define-syntax forloop
  (syntax-rules ()
    ((forloop start stop exps)
      (letrec ((aloop (lambda (astart astop aexps)
        (if (<= astart astop) (begin aexps (aloop (+ astart 1) astop aexps))))))
          (aloop start stop exps)))))

It would be nice to also be able to define it with an ellipsis, but this gives "a template contains repetition of constant form". I have read the macro section of the R5RS spec, though I will be rereading the template section soon:

(define-syntax forloop
  (syntax-rules ()
    ((forloop start stop exps ...)
      (letrec ((aloop (lambda (astart astop aexps ...)
        (if (<= astart astop) (begin aexps ... (aloop (+ astart 1) astop aexps ...))))))
        (aloop start stop exps ...)))))

I tried this first, but it runs for about 10 seconds before failing without any output... I'm using compileonline.com, so that may have something to do with it:

(define-syntax forloop
  (syntax-rules ()
    ((forloop start stop exps ...) 
      (if (<= start stop) 
        (begin exps ... (forloop (+ start 1) stop exps ...))))))

There is no issue if I never call forloop (which I think is because it never has to expand the macro), so the code I have used to test these is:

(forloop 6 8 (display "g"))

What am I doing wrong? I have successfully implemented a when statement (on my own, but there are dozens of examples out there and I had already seen a lot of them). I think the recursive nature of what I want to do and the ellipsis are messing me up.

1

There are 1 answers

8
C. K. Young On BEST ANSWER

Macros are expanded at compile time, thus something like (begin exps ... (forloop (+ start 1) stop exps ...)) will expand forloop again and again, regardless of what the value of (+ start 1) is (which is evaluated at runtime).

Perhaps the best you can do, at least with syntax-rules, is to use the macro to capture only the expressions to run, and use non-macro code to deal with the looping:

(define-syntax forloop
  (syntax-rules ()
    ((forloop start stop exps ...)
     (let ((j stop))
       (let loop ((i start))
         (when (<= i j)
           exps ...
           (loop (+ i 1))))))))

You can also use a do loop:

(define-syntax forloop
  (syntax-rules ()
    ((forloop start stop exps ...)
     (let ((j stop))
       (do ((i start (+ i 1)))
           ((> i j))
         exps ...)))))