我应该重写这个 LISP 宏还是修改它?

Should i rewrite this LISP macro or modify it?

我尝试编写一个宏,可以在函数 myfunc 中提供一个名称来创建一个 class 并创建一个 [=46] 的实例=].

代码如下:

(defmacro define-class (class-name)
  `(eval
    `(progn
       (defclass ,,class-name () ())
   (make-instance ,class-name))))

(defun myfunc (name)
   (define-class name))

我可以成功编译宏但不能编译函数。在这种情况下,我在编译时收到一条警告:

undefined variable: CLASS-NAME

如果我稍微修改一下宏,而不是写

   (make-instance ,class-name)

我写

(make-instance ,,class-name)

然后在这种情况下我可以编译两者,但是当 运行 (myfunc 'toto) 我得到以下错误:

The variable TOTO is unbound.

我试图弄清楚宏是如何用 macroexpand-1 展开的。使用 (make-instance ,class-name) 定义的第一个宏,它给了我以下结果:

(EVAL `(PROGN (DEFCLASS ,'TOTO NIL NIL) (MAKE-INSTANCE ,CLASS-NAME)))

而在用 *(make-instance ,class-name) 定义的第二个宏中,它给了我以下结果:

(EVAL `(PROGN (DEFCLASS ,'TOTO NIL NIL) (MAKE-INSTANCE ,'TOTO)))

但我想在我的情况下正确的扩展应该是这样的:

(EVAL `(PROGN (DEFCLASS ,'TOTO NIL NIL) (MAKE-INSTANCE 'TOTO)))

我如何修改或重写宏才能使其正常工作?

你想这样写代码:

(defun myfunc (name)
  (define-class name))

这实际上类似于:

(defun myfunc (name)
  (let ((name name))
    (eval `(defclass ,name () ()))
    (make-instance name)))

因此 DEFINE-CLASS 宏应该生成类似于上面的代码。

(defmacro define-class (name)
  (let ((name-sym (gensym "CLASS-NAME")))
    `(let ((,name-sym ,name))
       (eval `(defclass ,,name-sym () ()))
       (make-instance ,name-sym))))

使用它:

CL-USER 21 > (pprint (macroexpand-1 '(define-class name)))

(LET ((#:CLASS-NAME22897 NAME))
  (EVAL `(DEFCLASS ,#:CLASS-NAME22897 NIL NIL))
  (MAKE-INSTANCE #:CLASS-NAME22897))

CL-USER 22 > (myfunc 'baz42)
#<BAZ42 40202BBE73>

但是,没有理由它应该是一个宏!

一个正常的功能就好了...

(defun create-class-and-instance (name)
  (eval `(defclass ,name () ()))
  (make-instance name))

defclass 宏通常扩展为对创建 class 的实际依赖于实现的函数的调用。如果您可以改为调用该函数,则可以摆脱 eval。这样更直接一点,保持当前词法环境。

如果你加载closer-mop系统,你可以这样写你的函数:

(defun make-instance-from-new-class (class-name)
  (make-instance (closer-mop:ensure-class class-name)))