2016-07-26 108 views
2

如何扩展一个宏,在dolist等另一个循环宏中添加符号到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。

回答

2

Common Lisp中另一个典型的解决方案是写一个宏defcmds,这样你可以写:

(defcmds ("f1" 1) ("f2" t)) 

哪一个会令扩展为:

(progn 
    (defcmd "f1" t) 
    (defcmd "f2" t)) 

defcmd形式将扩大到

(progn 
    (defun ...) 
    (defun ...)) 
2

您需要:

(dolist (x '("f1" "f2")) 
    (eval `(make-cmd ,x t))) 

反引号表达`(make-cmd ,x t)只有构建语法。该语法未被评估。就像你写了(list 'make-cmd x t)一样。

如果为eval这个make-cmd宏调用的主要用途,它也可能变成一个功能:

(defun make-cmd-fun (cmd &optional search) 
    "Some function factory." 
    (let ((fn (intern (concat "fn-" cmd)))) 
    `(defun 
     ...))) 

现在你有一个简单的函数返回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只是调用该扩展器。

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)))