Re: which field to choose



"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!"
.



Relevant Pages

  • Re: Another @ Get command reference
    ... > automatically generates the commands that create lists. ... > FoxPro window, ... > on the current font. ... > If SHOW GETS is issued, the RANGE clause is re-evaluated. ...
    (microsoft.public.fox.helpwanted)
  • Parsing file format to ensure file meets criteria
    ... There is no limit on the number of clause lines. ... Header lines must precede clause lines. ... elements of the list are strings, even integers are converted to strings ... Question - how are nested lists indexed? ...
    (comp.lang.python)
  • RE: Dynamic Sorting
    ... > existing ORDER BY clause, ... > Dim strRowSource As String ... > ' Build ORDER BY clause, assign new RowSource, and Requery ... which will allow the user to sort (Based upon the current lists boxes ...
    (microsoft.public.access.forms)
  • List comprehensions and pattern matching
    ... list comprehensions and pattern matching are ... lists, ... to the right of the binding clause ...
    (comp.lang.scheme)
  • Re: Anyone with a command reference...
    ... a user-defined window. ... positions and sizes depend on the current font. ... the RANGE clause is re-evaluated. ... Lists are by default enabled when READ is issued. ...
    (microsoft.public.fox.programmer.exchange)