common lisp: how can a macro define other methods/

2019-04-04 23:24发布

问题:

I realized that a certain section of my code consists of groups of methods that look similar (like I have multiple trios: a helper function that gets called by two other functions meant for the programmer). I'm trying to write a macro that will define these three functions for me so that all I need to do is call the macro. But my attempt results in defuns and function calls that have quoted strings instead of the generated names as new symbols. What am I doing wrong?

Example (incorrect code)

(defmacro def-trio (base-name)
  (let 
    ((helper-name (format nil "helper-~a" base-name))
     (method-1 (format nil "~a-1" base-name))
     (method-2 (format nil "~a-2" base-name)))
    `(progn 
          (defun ,helper-name () 'helper-called)
          (defun ,method-1 () (,helper-name) '1-called)
          (defun ,method-2 () (,helper-name) '2-called))))

Now the following happens:

(def-trio my-trio)

==>

(PROGN (DEFUN "helper-MY-TRIO" () 'HELPER-CALLED)
       (DEFUN "MY-TRIO-1" () ("helper-MY-TRIO") '1-CALLED)
       (DEFUN "MY-TRIO-2" () ("helper-MY-TRIO") '2-CALLED))

Also, after I learn how to get this working, are there any extra gotcha's if I had this macro define other macros instead of other functions? I read How do I write a macro-defining macro in common lisp but I think my question is a little different because I'm asking about programmatically generated symbols/names. I'm open to being corrected though :) Thanks!

回答1:

Try this:

(defmacro def-trio (base-name)                                         ; changes:
  (let*                                                                ; 3.
    ((package (symbol-package base-name))                              ; 2.
     (helper-name (intern (format nil "HELPER-~a" base-name) package)) ; 1. 4.
     (method-1 (intern (format nil "~a-1" base-name) package))         ; 1.
     (method-2 (intern (format nil "~a-2" base-name) package)) )       ; 1.
    `(progn 
          (defun ,helper-name () 'helper-called)
          (defun ,method-1 () (,helper-name) '1-called)
          (defun ,method-2 () (,helper-name) '2-called) )))

The following changes were made to your original definition -- the first change is the crucial one:

  1. Interned each of computed symbol names into the same package as the base name using (intern ... package).
  2. Introduced the variable package which is bound to the package of the supplied base-name symbol.
  3. Changed let to let* to allow the newly introduced variable package to be referenced in subsequent variables.
  4. Changed the prefix of the helper method to upper case to match the convention for normal Lisp symbols.


回答2:

Use INTERN to turn the generated function name strings into symbols.