Lecture Notes: Macros(一)

2014-11-24 13:28:29 · 作者: · 浏览: 91

Lisp functions take Lisp values as input and return Lisp values. They are executed at run-time. Lisp macros take Lisp code as input, and return Lisp code. They are executed at compiler pre-processor time, just like in C. The resultant code gets executed at run-time. Almost all the errors that result from using macros can be traced to a misunderstanding of this fact.

1. Basic Idea: Macros take uneva luated Lisp code and return a Lisp form. This form should be code that calculates the proper value. Example:

(defmacro Square (X) 
  '(* ,X ,X))

This means that wherever the pre-processor sees (Square XXX) to replaces it with (* XXX XXX). The resultant code is what the compiler sees.

2. Debugging technique: macroexpand-1

When designing a function, you can type a call to the function into the Lisp Listener (prompt), and see if it returns the correct value. However, when you type a macro call into the Lisp Listener, two things happen: first, the macro is expanded into its resultant code, and then that code is eva luated. It is more useful during debugging to be able to examine the results of these two steps individually. The function macroexpand-1 returns the result of stage one of this process:

(macroexpand-1 '(Square 9)) ==> (* 9 9)

If in doubt, macroexpand-1 it out.

3. Purpose: To control eva luation of the arguments.

Since macros are so much harder to use than functions, a good rule of thumb is: don't use defmacro if defun will work fine. So, for example, there would be no reason to try to use a macro for Square: a function would be much easier to write and test. In Lisp, unlike in C, there is no need to use macros to avoid the very small runtime overhead of a function call: there is a separate method for that (the inline proclamation) that lets you do this without switching to a different syntax. What macros can do that functions cannot is to control when the arguments get eva luated. Functions eva luate all of their arguments before entering the body of the function. Macros don't eva luate any of their arguments at preprocessor time unless you tell it to, so it can expand into code that might not eva luate all of the arguments. For example, suppose that cond was in the language, but if wasn't, and you wanted to write a version of if using cond.

(defun Iff-Wrong (Test Then &optional Else)
  (cond
    (Test Then)
    (t    Else)))

The problem with this is that it always eva luates all of its arguments, while the semantics of if dictate that exactly one of the Then and Else arguments gets eva luated. For example:

(let ((Test 'A))
  (Iff-Wrong (numberp Test)
             (sqrt Test)
             Sorry, SQRT only defined for numbers))

will crash, since it tries to take (sqrt 'A).A correct version, with behavior identical to the built-in if (except that the real if only has one required arg, not two), would be:

(defmacro Iff (Test Then &optional Else)
  A replacement for IF, takes 2 or 3 arguments. If the first eva luates to
  non-NIL, eva luate and return the second. Otherwise eva luate and return
  the third (which defaults to NIL)
  '(cond
     (,Test ,Then)
     (t     ,Else)) )

A similar example would be NAND (Not AND), which returns true if at least one of the arguments is false, but, like the built-in and, does short-circuit eva luation whereby once it has the answer it returns immediately without eva luating later arguments.

(defmacro Nand (&rest Args)
  '(not (and ,@Args)))
4. Bugs:

(A) Trying to eva luate arguments at run-time

(B) eva luating arguments too many times

(C) Variable name clashes.

(A) Trying to eva luate arguments at run-tim