Doug Tolton wrote:
Problem 12-4 is:
Suppose, for some immensely peculiar reason, you want a form of LET
that does not evaluate initial value forms. Define LETQ such that
LETQ arranges the following translation:
(letq ((<variable1> <value1>)
(<variable2> <value2>)
...)
<body> )
translate to:
(let ((<variable1> '<value1>)
(<variable2> '<value2>)
...)
<body> )
[ snip ]
I
could just be missing something simple, but I don't understand how I
would take an arbitrary number of variables, and map only value
expressions so they aren't evaluated. Is there something simple I'm missing? Or is there an entire conceptual idea I'm not getting?
A macro is just a Lisp function that turns the list (LETQ ...) to (LET
...) according to the above schema. Write a function that does this,
and then turn it into a macro.
You might be confused by the DEFMACRO utility, which has built-in destructuring. Prior to things like DEFMACRO, Lisp macros were written
as ordinary functions that worked on the entire form. A LETQ macro
would receive just one argument: the entire source code of the macro
call: (LETQ ...)
With DEFMACRO, you have some built-in analysis. The form is matched
against a syntactic pattern, and pieces are assigned to variables.
(You can still receive the entire form in one piece using the &whole
lambda keyword).
With the preprocessing that DEFMACRO performs, it's no longer as
obvious to newbies that the macro is a function that turns one form
into another.
When there are repeated units to be handled, like ((<variable> <value>
...)) lists, newbies get confused, because they can't find the right
DEFMACRO syntax which would nicely destructure it without any
expenditure of programming work. ;)
The answer is that where DEFMACRO's destructuring runs out of steam,
you have to switch to ``manual'' list processing. For example, you can capture the entire ((<variable> <value>) ...) sublist, attaching it to
one variable:
(defmacro letq ((&rest variable-value-list) &body forms)
`(let ,(transform-variable-value-list variable-value-list)
,@forms))
Now you just need to write the function TRANSFORM-VARIABLE-VALUE-LIST.
This can be done using
(loop for (variable value) in variable-value-list
collecting `(,variable (quote ,value)))
so the whole macro becomes:
(defmacro letq ((&rest variable-value-list) &body forms)
(let ((transformed-list (loop for (variable value)
in variable-value-list
collecting `(,variable ',value))))
`(let ,transformed-list ,@forms)))
In other words, the function is nice and short so we roll it into the
body of the macro, thus sparing ourselves a lesson about eval-when. ;)
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 418 |
Nodes: | 16 (0 / 16) |
Uptime: | 15:53:11 |
Calls: | 8,795 |
Calls today: | 7 |
Files: | 13,300 |
Messages: | 5,966,648 |