在 dolist 中扩展宏
Expanding macros inside dolist
如何在另一个循环宏(如 dolist
内)扩展一个向 obarray
(此处为 defun
)添加符号的宏?例如,
(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。
你需要:
(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
...)))
现在你有一个简单的函数,它具有 returns defun
语法;当然必须 eval
-ed 才能生效。但是现在我们不必在使用宏的调用站点构造任何语法:
(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
只是调用那个扩展器。
Common Lisp 中另一个典型的解决方案是写一个宏defcmds
这样你就可以写:
(defcmds ("f1" 1) ("f2" t))
哪一个会扩展成:
(progn
(defcmd "f1" t)
(defcmd "f2" t))
其中defcmd
表格将扩展为
(progn
(defun ...)
(defun ...))
(其他人已经回答了你的直接问题。但这可能会回答你问题背后的问题并说明你真正想做的事情。如果没有,请参阅其他答案。)
您做不需要一个宏来做您想做的事。只需使用 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)
returns:
(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)))
如何在另一个循环宏(如 dolist
内)扩展一个向 obarray
(此处为 defun
)添加符号的宏?例如,
(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。
你需要:
(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
...)))
现在你有一个简单的函数,它具有 returns defun
语法;当然必须 eval
-ed 才能生效。但是现在我们不必在使用宏的调用站点构造任何语法:
(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
只是调用那个扩展器。
Common Lisp 中另一个典型的解决方案是写一个宏defcmds
这样你就可以写:
(defcmds ("f1" 1) ("f2" t))
哪一个会扩展成:
(progn
(defcmd "f1" t)
(defcmd "f2" t))
其中defcmd
表格将扩展为
(progn
(defun ...)
(defun ...))
(其他人已经回答了你的直接问题。但这可能会回答你问题背后的问题并说明你真正想做的事情。如果没有,请参阅其他答案。)
您做不需要一个宏来做您想做的事。只需使用 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)
returns:
(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)))