2016-07-26 103 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)))