如何在obarray
的另一个循环宏中扩展一个将符号添加到defun
(此处为dolist
)的宏?例如,
(defmacro make-cmd (cmd &optional search)
"Some function factory."
(let ((fn (intern (concat "fn-" cmd))))
`(defun ,fn (&optional args)
(interactive)
(let ((str (symbol-name ',fn))
,@(when search
'((dir "~"))))
(message "Called %S from %S" str
(or (and (bound-and-true-p dir) dir)
default-directory))))))
;; functions `fn-f1' and `fn-f2' aren't added to obarray
(dolist (x '("f1" "f2"))
`(make-cmd ,x t))
;; works like this
(make-cmd "f1" t)
我希望能够在编译和循环函数名时只需要宏。一个普通的lisp解决方案可能适合emacs-lisp。
答案 0 :(得分:3)
(其他人已经回答了你的直接问题。但这可能会回答你问题背后的问题,并谈谈你真正想做的事情。如果没有,请看其他答案。)
你 不根本不需要宏,做你想做的事。只需使用defalias
或fset
即可。每个都是功能。
(defun foo (cmd &optional search)
(let ((fn (intern (concat "fn-" cmd))))
(defalias fn `(lambda (&optional args)
(let ((str (symbol-name ',fn))
,@(when search
'((dir "~"))))
(message "Called %S from %S" str
(or (and (bound-and-true-p 'dir) dir)
default-directory)))))))
(dolist (x '("f1" "f2")) (foo x))
然后(symbol-function 'fn-f1)
返回:
(lambda (&optional args)
(let ((str (symbol-name 'fn-f1)))
(message "Called %S from %S" str (or (and (bound-and-true-p dir) dir)
default-directory))))
如果您使用词法绑定(即将局部变量绑定-*- lexical-binding: t -*-
放在定义此代码的文件的顶部,那么您不需要任何反引用。例如:
(defun foo (cmd &optional search)
(let ((fn (intern (concat "fn-" cmd))))
(defalias fn (lambda (&optional args)
(let ((str (symbol-name fn))
(dir (if search "~" default-directory)))
(message "Called %S from %S" str dir))))))
(dolist (x '("f1" "f2")) (foo x))
如果你这样做,那么每个函数fn-f1
和fn-f2
被定义为一个闭包,如下所示:
(symbol-function 'fn-f1)
(closure
((fn . fn-f1)
(search)
(cmd . "f1")
t)
(&optional args)
(let ((str (symbol-name fn))
(dir (if search "~" default-directory)))
(message "Called %S from %S" str dir)))
(foo "f3" :SEARCH)
定义了一个函数fn-f3
(一个闭包),它封装了一个自由变量search
与非nil
值:SEARCH
的绑定,以便局部变量dir
绑定到"~"
:
(symbol-function 'fn-f3)
(closure
((fn . fn-f3)
(search . :SEARCH) ;; <==============
(cmd . "f3")
t)
(&optional args)
(let ((str (symbol-name fn))
(dir (if search "~" default-directory)))
(message "Called %S from %S" str dir)))
答案 1 :(得分:2)
你需要:
(dolist (x '("f1" "f2"))
(eval `(make-cmd ,x t)))
反引号表达式`(make-cmd ,x t)
仅构造语法。该语法尚未评估。这与你写(list 'make-cmd x t)
一样。
如果此make-cmd
宏的主要用途是eval
,则可能会将其转换为函数:
(defun make-cmd-fun (cmd &optional search)
"Some function factory."
(let ((fn (intern (concat "fn-" cmd))))
`(defun
...)))
现在你有一个普通的函数,它返回defun
语法;那当然必须eval
- 以使其生效。但是现在我们不必在使用宏的调用站点构建更多语法:
(dolist (x '("f1" "f2"))
;; Note how at least the backquote is gone (but not eval).
(eval (make-cmd-fun x t))) ;; execute the defun form returned by make-cmd-fun
当我们有make-cmd-fun
时,我们可以根据它来定义宏版本:
(defmacro make-cmd (cmd &optional search)
(make-cmd-fun cmd search))
基本上我们已将原始宏扩展器功能设为make-cmd-fun
;宏make-cmd
只调用那个扩展器。
答案 2 :(得分:2)
Common Lisp中的另一个典型解决方案是编写宏rollBack()
,以便您可以编写:
defcmds
哪一个会扩展为:
(defcmds ("f1" 1) ("f2" t))
(progn
(defcmd "f1" t)
(defcmd "f2" t))
表格会扩展为
defcmd