扩展dolist内的宏

时间:2016-07-26 00:39:22

标签: function emacs macros lisp eval

如何在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。

3 个答案:

答案 0 :(得分:3)

(其他人已经回答了你的直接问题。但这可能会回答你问题背后的问题,并谈谈你真正想做的事情。如果没有,请看其他答案。)

根本不需要宏,做你想做的事。只需使用defaliasfset即可。每个都是功能

(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-f1fn-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