Page 1 of 1

using labels in macros

Posted: Sat Jul 25, 2009 9:51 am
by dbdkmezz
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!

Re: using labels in macros

Posted: Sun Jul 26, 2009 2:00 pm
by skypher
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

Re: using labels in macros

Posted: Sun Jul 26, 2009 2:22 pm
by Paul Donnelly
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.

Re: using labels in macros

Posted: Sun Jul 26, 2009 3:52 pm
by dbdkmezz
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

Re: using labels in macros

Posted: Mon Jul 27, 2009 1:18 am
by skypher
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

Re: using labels in macros

Posted: Mon Jul 27, 2009 10:19 am
by findinglisp
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.