2016-07-26 3 views
2

Как увеличить макрос, который добавляет символы в 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) 

Я хочу, чтобы иметь возможность требовать макросы при компиляции и перебора имен функций. Общепринятое решение, вероятно, будет прекрасно адаптироваться к 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).

Если первичное использование для этого make-cmd макровызов для eval, она также может быть превращена в функцию:

(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

(Другие ответили на ваши прямые вопросы Тион. Но это может ответить на вопрос, стоящий за вашим вопросом, и поговорить с тем, что вы действительно пытаетесь сделать. Если нет, смотрите другие ответы.)

Вы сделать не нужен макрос вообще, чтобы делать то, что вы хотите. Просто используйте 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)))