Common lisp 为什么这个宏以一种方式工作而不是以另一种方式工作?

Common lisp 为什么这个宏以一种方式工作而不是以另一种方式工作?,common-lisp,sbcl,Common Lisp,Sbcl,这是我试图使用的“clx user callable.lisp”中定义的宏 (in-package :clx-gui) (defmacro get-callback-wrapper (callback) (declare (ignorable callback)) (let* ((func-name (gensym)) (wrapper-name (intern (format nil "WRAPPER-~a" func-name) ))) `(defun ,

这是我试图使用的“clx user callable.lisp”中定义的宏

(in-package :clx-gui)

(defmacro get-callback-wrapper (callback)
  (declare (ignorable callback))
  (let* ((func-name (gensym))
         (wrapper-name (intern (format nil "WRAPPER-~a" func-name) )))
    `(defun ,wrapper-name (caller-instance)
       (funcall ,callback) ;; User callbacks wont have arguments
       (closemenu caller-instance))))
我用这种方式调用这个宏,它工作正常

(in-package :clx-gui-test-app)

(create-user-menuitem "MyUserMenu" "MyEntryDialog"
                      (get-callback-wrapper 'my-callback))

(create-user-menuitem "MyUserMenu" "MyChoiceDialog"
                      (get-callback-wrapper 'my-callback2))

(create-user-menuitem "MyUserMenu" "MyMessageDialog"
                      (get-callback-wrapper 'my-callback3))
如果我将代码更改为以这种方式使用宏,通过将回调的符号名传递给调用宏的函数,它不会返回不同的包装函数,而是始终返回相同的包装函数。调用宏的函数与宏定义位于同一文件和包中

(in-package :clx-gui-test-app)
(create-user-menuitem "MyUserMenu" "MyEntryDialog" 'my-callback)
(create-user-menuitem "MyUserMenu" "MyChoiceDialog" 'my-callback2)
(create-user-menuitem "MyUserMenu" "MyMessageDialog" 'my-callback3)
我已经尝试将包添加到宏定义中,但没有帮助

(wrapper-name (intern (format nil "WRAPPER-~a" func-name)
                      (symbol-package callback) )))
我做错了什么

我正在使用SBCL-1.0.57和Slime

CL-USER>
(defparameter foo1 (gensym))
FOO1
CL-USER> 
foo1
#:G4619
CL-USER> 
(defparameter foo2 '#:G4619)
FOO2
CL-USER> 
foo2
#:G4619
CL-USER> 
(eq foo1 foo2)
NIL
CL-USER> 
~           
或者另一个有趣的练习:

(defmacro make-fun ()  
  `(defun ,(intern (format nil "WRAPPER-~a" (gensym))) ()
     'bar))

CL-USER> 
(make-fun)
WRAPPER-G4726
CL-USER> 
(make-fun)
WRAPPER-G4730
CL-USER> 
(make-fun)
WRAPPER-G4734
CL-USER> 
(make-fun)
WRAPPER-G4738
CL-USER> 
(defun WRAPPER-G4745 ()
  'foo)
WRAPPER-G4745
CL-USER> 
(make-fun)
WRAPPER-G4745
CL-USER> (wrapper-G4745)
BAR
CL-USER>
哦,天哪,我们刚刚重写了那个函数

如果您想用某种前缀名称来表示gensym,请在gensym调用中执行(作为可选参数)。但所有这些都只是一个练习,b/c我仍然会在OP问题中使用lambda

下面是一个(IMO)更简单的替代实现,它应该能够满足您的需要:

(defun get-callback-wrapper (callback)
  (lambda (caller-instance)
    (funcall callback) 
    (closemenu caller-instance)))

这将生成我认为您需要的词法闭包。

这是因为宏在编译函数时展开,而不是在调用函数时展开。将
创建用户菜单项
函数包装为
(eval when(:compile toplevel:load toplevel:execute)
。请注意,defun/gensym方法不能保证创建唯一的函数名,b/c两个gensym不能相等且具有相同的打印值。如果您只是试图定义一个永远不会使用的唯一函数名,而您并不真正关心名称,为什么不使用lambda?@claytontstanley:two gensym为什么会这样ld是否具有相同的打印值?请注意,正如所写的,没有理由需要将其作为宏来实现。函数可以很好地实现,b/c您提供给它的唯一参数只是一个带引号的符号。请使用(eval when(:compile toplevel:load toplevel:execute)包装您的create user menuitem函数–塞巴斯蒂安·贝尼特斯(Sebastián Benítez)2天前没有任何效果。我误解了宏的使用方式。我认为使用不同的回调符号创建用户菜单项的调用应该会导致不同的回调函数,这在我看来是不正确的。在defun中使用lambda是完成以下任务的正确方法我正在尝试。谢谢大家的帮助。@pbowler很高兴能提供帮助。我鼓励你们继续写和学习更多关于常见lisp宏的知识。我在这个主题上的书籍推荐是关于lisp和Let Over Lambda的;你们最初的帖子涉及了Let Over Lambda中讨论的许多主题,仅供参考