2

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 Answer 1

2

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 ...)))))
Sign up to request clarification or add additional context in comments.

8 Comments

Thanks, that works, and the compile time vs. run time makes sense, but I'm failing to see how your first example is fundamentally different than my first example... And yours works and mine doesn't lol.
The fundamental difference is that loop in my version is not a macro.
Is the letrec creating the macro? Or maybe the lambda? I didn't think it was a macro.
Oops, I was looking at your last example, not the first one. The fundamental difference with your first example is that I'm not passing the expressions into the recursive calls.
@MillieSmith your first code turns (forloop a b c) into (letrec ((aloop (lambda(x y z) (if (<= x y) (begin z (aloop (+ x 1) y z)))))) (aloop a b c)). So c is evaluated once, and its value is passed into the aloop call which does nothing with it. Your aloop is just a function - and so gets its arguments values; Chris's macro creates a named let construct into which your expressions are placed, as expressions.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.