Saturday, August 21, 2010

for and for-permutation

When I'm prototyping scheme programs, I usually work in Racket. It's a nice environment and most things I need are ready. It's a battery included framework and I love that.

However, sometimes I think that when I used to play with gambit (pun intended) my scheme was better. I somewhat crafted minimal tools to solve the problem instead of fitting pre-made blocks. It's a bit on the design pattern as coarse grained way of thinking to engineering problems[0] line of thought.

Anyway, I used in my programs Racket for form too much and now I have troubles in porting them to other schemes. So I decided to implement the for form with syntax-rules.

(define-syntax for-permutation
  (syntax-rules ()
    [(_ () s1 s2 ...)
     (begin s1 s2 ...)]
    [(_ ([v1 l1] [v2 l2] ...) s1 s2 ...)
     (for-each
       (lambda (v1)
         (for-permutation ([v2 l2] ...)
           s1 s2 ...)) l1)]))

(define-syntax for
  (syntax-rules ()
    [(_ () s1 s2 ...)
     (begin s1 s2 ...)]
    [(_ ([v1 l1] [v2 l2] ...) s1 s2 ...)
     (for-each
       (lambda (v1 v2 ...)
         (begin s1 s2 ...))
       l1 l2 ...)]))

For-permutation works a bit like list comprehensions in Python:

[In]  [i+j for i in range(4) for j in range(3)]
[Out] [0, 1, 2, 1, 2, 3, 2, 3, 4, 3, 4, 5]

On the other hand for works just iterating on the sequences "together":

(for ([a '(1 2 3)] [b '(6 7 8)]) (display (+ a b)))
7911

Of course, since it depends on for-each, sequences must have the same length.

---
[0] more on this another day...

10 comments:

Marko Nervo said...

(I don't speak english very well, I'm sorry...)
Hi. Here my implementation:

(define-syntax for
(syntax-rules (in)
[(_ ([v in l] ...) e ...)
(for-each
(lambda (v ...) e ...)
l ...)]))

I didn't do for-permutation 'cause IMO it is equal to a many nested for loop, but I've a question about your for-permutation implementation.
Why you write for at the fifth line instead of _ or for-permutation and why at the 8th line you "call" for instead of for-permutation?
Isn't more correct to do:
(define-syntax for-permutation
(syntax-rules ()
[(_ () s1 s2 ...)
(begin s1 s2 ...)]
[(_ ([v1 l1] [v2 l2] ...) s1 s2 ...)
(for-each
(lambda (v1)
(for-permutation ([v2 l2] ...)
s1 s2 ...)) l1)]))

?

Bye,
Marco.

Ps: se non capisci il mio inglese maccheronico poi posso farti anche la traduzione =)

Marko Nervo said...
This comment has been removed by the author.
Marko Nervo said...

...and here my range implementation:

(define range
(case-lambda
[(n) (let loop ([x 0]) (if (>= x n) '() (cons x (loop (+ x 1)))))]
[(n m) (let loop ([x n]) (if (>= x m) '() (cons x (loop (+ x 1)))))]
[(n m s) (let loop ([x n]) (if (>= x m) '() (cons x (loop (+ x s)))))])

What do you think?

-Marko

Unknown said...

Your implementation of for is structurally equivalent to the one I gave. You just specified less elements before the ellipses, which is correct, by the way.

You added the requirement to use an "in like operator" from a syntactical point of view. I understand that makes for more Pythonish, though I don't feel like it's a strong requirement or it does add clarity.

Regarding for-permutation it *is* equivalent to multiple for loops; the definition makes it clear.

However, the point of extending the syntax of a language is just to introduce constructs which are equivalent to older constructs but are easier or more confortable to use.

Regarding your question: the for in the 8th line is not a real issue. It's a copy and paste error and should be _ or for-permutation: however the real value of the thing is basically ignored in that definition.

The real problem is on line 10: that is the very copy and paste error, but this time is a real issue as for-permutation does not do what it should for more variable definitions.

Eventually, about your range implementation I can say that I would not write it that way for two reasons:

1. if I use case-lambda, I would have the simpler cases call the more complex one (the one with start, end and step)
2. I would just use unfold.

range is a pythonism: in fact, it just a particular case of a anamorphism. A slightly more generic anamorphism is unfold.

Unfold can easily be instantiated to have range. On the other hand, I would not have range lists created explicitly and then processed.

I would just combine the catamorphism working on the list and the anamorphism generating the list.

Unknown said...

And of course, many thanks for pointing out the error in for-permutation definition. Now it's fixed.

Marko Nervo said...

Another rapid question: is better to use the form:
(lambda (v ...) (begin e ...))
or the form:
(lambda (v ...) e ...)
?

Thanks for all,
Marco.

Marko Nervo said...

For range made with case-lambda did you mean that:
(define range
(case-lambda
[(n) (range 0 n 1)]
[(n m) (range n m 1)]
[(n m s) (let loop ([x n]) (if (>= x m) '() (cons x (loop (+ x s)))))]))
?

Thanks again,
Marco.

Unknown said...

(lambda (v ...) (begin e ...))

vs.

(lambda (v ...) e ...)

I haven't checked the standard, but Dybvig explicitly states that the body of a lambda is treated as if it was enclosed in a begin form. So I think it is correct to say that in R6RS they are just the same thing.

I think I used the more verbose version because of cut and paste, but it's hard to tell months later.

Unknown said...

>> For range made with case-lambda did you mean that:

Yes. That is what I meant.

Marko Nervo said...

Thanks! ;)