Reputation: 14911
I'm building an alphametic solver, and I'd like to make a macro that substitutes numbers into a symbol template.
Here is a self-contained example:
(defparameter *symbol-positions* '(#\H #\T #\S #\R #\A #\U #\E #\O #\W #\N))
(defmacro word-to-number (symbols lst)
`(tonumber (list ,@(loop for symbol in symbols
when (not (eql symbol #\ ))
collect `(nth ,(position symbol *symbol-positions*) ,lst )))))
(defparameter num '(0 1 2 3 4 5 6 7 8 9))
(defparameter east '(#\ #\E #\A #\S #\T))
The following call works:
(word-to-number (#\ #\E #\A #\S #\T) num)
But this one doesn't:
(word-to-number east num) ;=> The value EAST is not of type LIST
Is there a way I can modify the macro to take a variable for the SYMBOLS parameter? ,symbols
doesn't work, and neither does `(,@symbols)
Upvotes: 2
Views: 54
Reputation: 48745
When you do:
(word-to-number east num)
The macro expander gets called with the arguments being east
and num
. They won't be lists and number for you macro. Only for the resulting code will they be evaluated in a contex which yields values.
A macro is syntax transformation. eg.
(cond (p1 c1)
(p2 c2)
(t a))
; ==
(if p1
c1
(if p2
c2
a))
It is regardless if p1
is (< a b)
or my-boolean-value
. A macro just places the expressions there without having to know what a
, b
or my-boolean-value
is.
So tell me.. How should the expansion look with (word-to-number east num)
? Perhaps it shouldn't be a macro at all? eg.
(defun word-to-number (symbols lst)
(tonumber (loop :for symbol :in symbols
:when (not (eql symbol #\ ))
:collect (nth (position symbol *symbol-positions*) lst))))
UPDATE
(defmacro word-to-number (symbols lst)
`(tonumber (loop :for symbol :in ,symbols
:with clst := ,lst
:when (not (eql symbol #\ ))
:collect (nth (position symbol *symbol-positions*) clst))))
You might notice that I'm storing lst
in a variable clst
and I do it after evaluating symbols
. The reason is that when you expect arguments to be evaluated you expect them to be evaluated in order and only once unless repeated evaluation is a feature of the macro like loop
does. Eg. this should only print "Oh, happy day" once:
(word-to-number (progn (princ "Oh") east) (progn (princ ", happy day!") num))
Upvotes: 4