我应该重写这个 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)))
我尝试编写一个宏,可以在函数 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)))