Re: which field to choose
- From: Pascal Bourguignon <usenet@xxxxxxxxxxxxxxxxx>
- Date: Mon, 03 Apr 2006 17:21:51 +0200
"arnuld" <arnuld3@xxxxxxxxx> writes:
(MACROS are vary hard to understand and much harder to write).
No, not really. There's no mystery in them.
Macros are mere functions that take a list and return a form.
A form is either an atom or a list.
For example, we'll write a COND macro, which will transform lists like:
(cond ((= a 1) (print 1))
((= b 2) (print 2) (print a))
(t (print 0)))
into lists like:
(if (= a 1)
(print 1)
(if (= b 2)
(progn (print 2)
(print a))
(print 0)))
Let's write a function that we will call as:
(fmycond '(cond ((= a 1) (print 1))
((= b 2) (print 2) (print a))
(t (print 0))))
to produce this result:
(defun fmycond (whole)
(let* ((clauses (reverse (rest whole)))
(else (if (eql 't (first (first clauses)))
(let ((clause (pop clauses)))
(if (null (rest (rest clause)))
(second clause)
(cons 'progn (rest clause))))
nil)))
(loop for clause in clauses
do (setf else (list 'if (first clause)
(if (null (rest (rest clause)))
(second clause)
(cons 'progn (rest clause)))
else))
finally (return else))))
(fmycond '(cond ((= a 1) (print 1))
((= b 2) (print 2) (print a))
(t (print 0))))
--> (IF (= A 1) (PRINT 1) (IF (= B 2) (PROGN (PRINT 2) (PRINT A)) (PRINT 0)))
Now, we just want to let know to the compiler that this function must
be called to process the COND forms we put in your programs:
(shadow 'cond) ; because it already exist in CL...
(defmacro cond (&whole whole &rest clauses)
(declare (ignore clauses))
(fmycond whole))
(macroexpand-1 '(cond ((= a 1) (print 1))
((= b 2) (print 2) (print a))
(t (print 0))))
-->
(IF (= A 1) (PRINT 1) (IF (= B 2) (PROGN (PRINT 2) (PRINT A)) (PRINT 0))) ;
T
(let ((a 3) (b 2))
(cond ((= a 1) (print 1))
((= b 2) (print 2) (print a))
(t (print 0))))
prints:
2
3
returns:
3
Ok? Nothing special with macros.
Now, we can optimize it a little. As you can see, the lambda list of
the defmacro, is used to destructure the source list. So you don't
have to do it again in your function, and in general, we don't need to
keep the whole source list.
(defmacro cond (&rest clauses)
(fmycond clauses))
(defun fmycond (clauses)
(let* ((clauses (reverse clauses)) ; not (rest whole)
(else (if (eql 't (first (first clauses)))
(let ((clause (pop clauses)))
(if (null (rest (rest clause)))
(second clause)
(cons 'progn (rest clause))))
nil)))
(loop for clause in clauses
do (setf else (list 'if (first clause)
(if (null (rest (rest clause)))
(second clause)
(cons 'progn (rest clause)))
else))
finally (return else))))
And of course, when you have a function that just call another, and
you don't really need the other, you can move the code from the other
to the one:
(defmacro cond (&rest clauses)
(let* ((clauses (reverse clauses))
(else (if (eql 't (first (first clauses)))
(let ((clause (pop clauses)))
(if (null (rest (rest clause)))
(second clause)
(cons 'progn (rest clause))))
nil)))
(loop for clause in clauses
do (setf else (list 'if (first clause)
(if (null (rest (rest clause)))
(second clause)
(cons 'progn (rest clause)))
else))
finally (return else))))
(macroexpand-1 '(cond ((= a 1) (print 1))
((= b 2) (print 2) (print a))
(t (print 0))))
-->
(IF (= A 1) (PRINT 1) (IF (= B 2) (PROGN (PRINT 2) (PRINT A)) (PRINT 0))) ;
T
No mystery.
One final point. Since we are building a lot of new lists (and
sublists) in this kind of functions (the kind of functions that build
lists, this has nothing to do with macros), we can use a shortcut
notation instead of using all these LIST and CONS function calls:
(cons 'progn (rest clause))
can be written as:
`(progn ,@(rest clause))
(list 'if (first clause)
(if (null (rest (rest clause)))
(second clause)
(cons 'progn (rest clause)))
else)
can be written as:
`(if ,(first clause)
,(if (null (rest (rest clause)))
(second clause)
`(progn ,@(rest clause)))
,else)
--
__Pascal Bourguignon__ http://www.informatimago.com/
"You question the worthiness of my code? I should kill you where you
stand!"
.
- Follow-Ups:
- Re: which field to choose
- From: Ken Tilton
- Re: which field to choose
- References:
- which field to choose
- From: arnuld
- which field to choose
- Prev by Date: Re: DST for the EU
- Next by Date: Re: packages, load paths and environment variables
- Previous by thread: Re: which field to choose
- Next by thread: Re: which field to choose
- Index(es):
Relevant Pages
|