using labels in macros

Discussion of Common Lisp

using labels in macros

Postby dbdkmezz » Sat Jul 25, 2009 9:51 am

Hey,

I've recently started to learn lisp, and to exercise what I've learnt I've started writing a simple connect 4 game. I want to write a macro to check if a number of positions are on the board. Using inspiration from the sbcl source code for the AND macro as a example of writing macros which work on an unlimited number of parameters using &rest, I first wrote this:
Code: Select all
(defmacro on-board-p (&rest positions)
  (cond ((endp positions) t)
   ((endp (rest positions))
    `(and (>= ,(first positions) 0)
          (< ,(first positions) +board-size)))
   (t
    `(and (>= ,(first positions) 0)
          (< ,(first positions) +board-size)
          (on-board-p ,@(rest positions))))))


But then I saw that the (>= ,(first positions) 0) and the (< ,(first positions) +board-size) tests were being repeated, so I tried to group those tests together using labels:
Code: Select all
(defmacro on-board-p (&rest positions)
  `(labels ((test (p)
         (and (>= p 0)
         (< p +board-size))))
     ,(cond ((endp positions) t)
       ((endp (rest positions)) `(test ,(first positions)))
       (t
        `(and (test ,(first positions))
         (on-board-p ,@(rest positions)))))))


The problem with that is that I've got ` marks nested in a , mark, nested in a ` mark, which I've read is a bad idea (and I can see why: it could easily get confusing). Also, the macro is recursive, and I'm going to get a copy of my labels code for every recursion, which seems very foolish. Is there an easier way to use functions defined with LABELS in macros? I could just write and extra test function, but I'd rather know how to do this without cluttering up the namespace.

By the way, I originally tried to start writing this as a function, but I couldn't work out how to send (rest positions) through to the recursive call, it would just complain that was was being sent through for the recursion was 'not of type REAL'. If this can be more easily written as a function, then I'd like to hear how. (Although even if in this case I'm better off with a function, I'd still like to know how to mix LABELS with macros in cases like this, just for educational purposes.)

Thanks!
dbdkmezz
 
Posts: 6
Joined: Sat Jul 25, 2009 9:26 am

Re: using labels in macros

Postby skypher » Sun Jul 26, 2009 2:00 pm

You really need a function instead of a macro here.

And you want APPLY to pass through your argument list to a recursive call of the function.

HTH,

Leslie
skypher
 
Posts: 34
Joined: Thu Jul 03, 2008 6:12 am

Re: using labels in macros

Postby Paul Donnelly » Sun Jul 26, 2009 2:22 pm

Yeah, this definitely doesn't call for a macro. The reason it's getting confusing is precisely because you're attempting to use a macro for a function's job.

skypher wrote:And you want APPLY to pass through your argument list to a recursive call of the function.

Personally, I'd drop the &rest and just pass a list to the thing in the first place.
Paul Donnelly
 
Posts: 148
Joined: Wed Jul 30, 2008 11:26 pm

Re: using labels in macros

Postby dbdkmezz » Sun Jul 26, 2009 3:52 pm

skypher wrote:And you want APPLY to pass through your argument list to a recursive call of the function.

Ahhh, APPLY, that's exactly what I needed! Thanks, now I can write a function to do it all, rather than that messy macro.

However, while waiting for my message to be approved by a moderator (this is my first post) I dug around the sbcl source code some more and was reminded of the EVERY function, which provides an even better solution. This is what I ended up with:
Code: Select all
(defun on-board-p (&rest positions)
  (every #'(lambda (p)
        (and (>= p 0)
        (< p +board-size)))
    positions))

Much better :)

It seems like every time I write a long and complicated function or macro in Lisp I soon discover that there is something built in to the language already, which solves my problem much more cleanly and easily. It's great, it's as if all the problems have already been solved for me, I just have to discover what the solutions are!

Thanks for the replies

Paul
dbdkmezz
 
Posts: 6
Joined: Sat Jul 25, 2009 9:26 am

Re: using labels in macros

Postby skypher » Mon Jul 27, 2009 1:18 am

Yeah, much better indeed. :D

I'd consider abstracting it even more. Using the popular RCURRY macro from Alexandria (or CURRY-AFTER from Metatilities) we can define

Code: Select all
(defun between (x min max)
  (and (>= x min) (<= x max)))

(defun on-board-p (&rest positions)
  (every (rcurry #'between 0 (1- +board-size)) positions))


It seems like every time I write a long and complicated function or macro in Lisp I soon discover that there is something built in to the language already, which solves my problem much more cleanly and easily. It's great, it's as if all the problems have already been solved for me, I just have to discover what the solutions are!

The great thing about CL is that it gives you most of the basic tools you need to tackle your problems.
This way you can concentrate on solving your actual problem.

Leslie
skypher
 
Posts: 34
Joined: Thu Jul 03, 2008 6:12 am

Re: using labels in macros

Postby findinglisp » Mon Jul 27, 2009 10:19 am

dbdkmezz wrote:It seems like every time I write a long and complicated function or macro in Lisp I soon discover that there is something built in to the language already, which solves my problem much more cleanly and easily. It's great, it's as if all the problems have already been solved for me, I just have to discover what the solutions are!


This is very common for Lisp newbies. It's simply part of the learning cycle. To accelerate you through this period more rapidly, I would recommend reading as much good Lisp code as you can. There was a thread here recently with suggestions.
Cheers, Dave
Slowly but surely the world is finding Lisp. http://www.findinglisp.com/blog/
findinglisp
 
Posts: 440
Joined: Sat Jun 28, 2008 7:49 am
Location: Austin, TX


Return to Common Lisp

Who is online

Users browsing this forum: No registered users and 8 guests

cron