2016-07-26 46 views
2

Wie kann ich ein Makro erweitern, das Symbole zu dem obarray (hier defun) innerhalb eines anderen Schleifenmakros wie dolist hinzufügt? Zum BeispielErweitern von Makros in 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) 

Ich möchte in der Lage sein, nur die Makros beim Kompilieren und Schleifen über Funktionsnamen benötigen. Eine Common-Lisp-Lösung wäre wahrscheinlich in Ordnung, um sich an Emacs-Lisp anzupassen.

Antwort

2

Eine weitere typische Lösung in Common Lisp ist ein Makro defcmds zu schreiben, so dass Sie schreiben können:

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

Wo die defcmd Formen in erweitern würde:

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

Welche würde man in erweitern machen

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

Sie benötigen:

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

Der Ausdruck Backquote `(make-cmd ,x t) nur die Syntax-Konstrukte. Diese Syntax wird nicht ausgewertet. Es ist das Gleiche, als ob Sie (list 'make-cmd x t) geschrieben hätten.

Wenn die primäre Verwendung für diese make-cmd Makroaufrufe für eval, es auch in eine Funktion umgewandelt werden könnte:

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

Jetzt haben Sie eine einfache Funktion, die defun Syntax gibt; und das muss natürlich eval -ed sein, um es umzusetzen. Aber jetzt müssen wir an dem Aufrufort mehr Syntax nicht bauen, wo wir das Makro zu verwenden:

(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 

Wenn wir make-cmd-fun haben, können wir dann definieren die Makro-Version in Bezug auf sie:

(defmacro make-cmd (cmd &optional search) 
    (make-cmd-fun cmd search)) 

Grundsätzlich haben wir die rohe Makroexpanderfunktion als make-cmd-fun verfügbar gemacht; das Makro make-cmd nennt diesen Expander einfach.

3

(Andere haben Ihre direkten Fragen beantwortet tion. Aber das könnte die Frage beantworten, die hinter Ihrer Frage steht, und mit dem sprechen, was Sie wirklich tun wollen. Falls nicht, die anderen Antworten sehen.)

Sie tun nicht brauchen ein Makro überhaupt, zu tun, was Sie wollen. Verwenden Sie einfach defalias oder fset. Jeder ist eine Funktion.

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

Dann (symbol-function 'fn-f1) kehrt:

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

Und wenn Sie verwenden lexikalische Bindung (dSetzen Sie die lokale Variable -*- lexical-binding: t -*- an den Anfang der Datei, in der dieser Code definiert ist. Dann brauchen Sie kein Backquotting mehr. Zum Beispiel:

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

Wenn Sie das tun, dann jede Funktion fn-f1 und fn-f2 als Verschluss definiert ist, die wie folgt aussieht:

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

Und (foo "f3" :SEARCH) definiert eine Funktion fn-f3 (einen Verschluss), der verkapselt eine Bindung der ansonsten freien Variablen search an den nicht nil Wert :SEARCH, so dass die lokale Variable dir an "~" gebunden wird:

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