2016-03-24 11 views
0

Ich habe immer Verwirrung über macro in Emacs. Es gibt viele Dokumente über die Verwendung von macro. Aber die Dokumente erwähnen nur die Oberfläche und Beispiele sind oft zu einfach. Außerdem ist es sehr schwierig, selbst nach macro zu suchen. Es wird Tastaturmakroergebnisse ergeben.Compile Expansion Mysterium von Makro in Elisp

Die Leute sagen immer Makro wird zur Kompilierzeit erweitert und es ist so schnell wie das Schreiben der gleichen Kopie des Codes direkt. Ist das immer wahr?

Ich beginne mit dem Beispiel when.

(pp-macroexpand-expression 
'(when t 
    (print "t"))) 

;; (if t 
;;  (progn 
;;  (print "t"))) 

Wenn wir when beim Kompilieren verwenden, wird die (if t .....) direkt an unseren Code eingefügt, nicht wahr?

Dann finde ich ein komplizierteres Beispiel dotimes von subr.el. Ich vereinfacht den Code ein bisschen:

(defmacro dotimes (spec &rest body) 

    (declare (indent 1) (debug dolist)) 

    (let ((temp '--dotimes-limit--) 
     (start 0) 
     (end (nth 1 spec))) 

    `(let ((,temp ,end) 
      (,(car spec) ,start)) 
     (while (< ,(car spec) ,temp) 
     ,@body 
     (setq ,(car spec) (1+ ,(car spec)))) 
     ,@(cdr (cdr spec))))) 

Was meine Aufmerksamkeit der (end (nth 1 spec)) ist. Ich denke dieser Teil muss zur Laufzeit gemacht werden. Wenn dies zur Laufzeit ausgeführt wird, bedeutet dies, dass die Codeerweiterung nicht zur Kompilierzeit durchgeführt werden kann. Um es zu testen, habe ich die dotimes ein bisschen modifiziert und die Datei kompiliert.

(defmacro my-dotimes (spec &rest body) 
    (declare (indent 1) (debug dolist)) 
    (let ((temp '--dotimes-limit--) 
     (start 0) 
     ;; This is my test 
     (end (and (print "test: ")(print (nth 1 spec)) (nth 1 spec)))) 
    `(let ((,temp ,end) 
      (,(car spec) ,start)) 
     (while (< ,(car spec) ,temp) 
     ,@body 
     (setq ,(car spec) (1+ ,(car spec)))) 
     ,@(cdr (cdr spec))))) 

(provide 'my) 

Ergebnis

(require 'my) 
(my-dotimes (var 3) 
    (print "dotimes")) 

;; "test: " 
;; 3 
;; "dotimes" 
;; "dotimes" 
;; "dotimes" 

der Tat ist mein Test-Anweisung zur Laufzeit erfolgen. Wenn es um die Expansion:

(pp-macroexpand-expression 
'(my-dotimes (var 3) 
    (print "dotimes"))) 

;; (let 
;;  ((--dotimes-limit-- 3) 
;;  (var 0)) 
;; (while 
;;  (< var --dotimes-limit--) 
;;  (print "dotimes") 
;;  (setq var 
;;   (1+ var)))) 

Überraschenderweise mein Testteil verloren.

So ist dotimes zur Laufzeit erweitert?

Wenn ja, bedeutet das, dass es den Vorteil eines generischen macro verliert, dass macro so schnell ist, wie der gleiche Code direkt schreiben?

Was macht der Interpreter, wenn er macro erfüllt, die Laufzeitkomponenten haben und?

Antwort

1

Ich habe den Eindruck, dass Sie die Datei mit dem Makro Definition Byte kompiliert, aber nicht Byte-kompilieren die Datei, die das Makro aufruft?

Es ist die Aufrufe zu einem Makro, die erweitert werden.

Die Erweiterung erfolgt zur Kompilierzeit, wenn der aufrufende Code kompiliert wird; zur Ladezeit ("eifrige" Makroerweiterung, nur in den letzten Emacs-Versionen), wenn der geladene Code nicht kompiliert wurde; und zur Laufzeit, wenn eine eifrige Erweiterung nicht möglich oder nicht verfügbar war.

Offensichtlich, wenn ein Aufruf eines Makros zur Laufzeit ausgewertet wird, gab es nie eine Möglichkeit, es im Voraus zu erweitern, so dass Expansion sofort zur Laufzeit erfolgt.

Die Makroerweiterung zur Kompilierzeit (oder Ladezeit) ist möglich, weil Makroargumente nicht ausgewertet werden. Es gibt nichts problematisches über (nth 1 spec), das zur Expansionszeit ausgewertet wird, da der Wert spec das unbewertete Argument ist, das in dem ursprünglichen Aufruf des Makros angezeigt wurde.

das heißt, wenn (dotimes (var 3)) das spec Argument erweitert die Liste (var 3) und (nth 1 spec) daher Expansionszeit 3 ausgewertet wird.

Aus Gründen der Klarheit (wie Zahlen Angelegenheiten hier verwirren könnte), wenn es (nth 0 spec) gewesen wäre, dann wäre es auf das Symbol ausgewertet var, insbesondere nichtvar ‚s Wert als Variable (die etablierten lässt sich nicht zur Expansionszeit).

So (nth 1 spec) - und in der Tat jede Manipulation der unbewerteten Argumente an das Makro übergeben - kann absolut bei der Expansion Zeit eingerichtet werden.

(edit: Warten Sie, wir bedeckt dies in Can I put condition in emacs lisp macro?)

Wenn Sie fragen, was passiert, wenn das Makro mit einem Wert etwas zu expansions Zeit tut, die zur Laufzeit dynamisch ist, ist die Antwort Einfach, dass es den Expansionszeitwert sieht, und folglich kann der erweiterte Code abhängig davon variieren, wann die Erweiterung aufgetreten ist. Es gibt nichts, was Sie davon abhält, Makros zu schreiben, die sich so verhalten, aber es ist generell nicht ratsam.

+0

Endlich habe ich alles richtig gemacht. Ich habe lange verwirrt, weil ich nie eine Datei kompiliert habe, die das Makro verwendet. Ich habe nur die Makrodatei selbst byte-kompiliert. Und wenn jemand eine Datei byte-kompiliert, MUSS Makro erweitert werden. Das sind die zwei Punkte, mit denen ich vorher Probleme hatte. Sie können nicht wissen, wie viel ich Ihrer Antwort dankbar war. – tom

0

Just my two cents ...

Die Leute sagen immer Makro bei der Kompilierung erweitert wird und es so schnell wie die gleiche Kopie des Codes direkt zu schreiben. Ist das immer wahr?

Es ist nicht immer immer wahr.

Wenn Sie eine Funktion kompilieren, deren Code einen Aufruf an ein Makro enthält, ist es wahr.

Aber, wenn Sie es nicht kompilieren, ist es fast wahr.

Angenommen, Sie eine Funktion mit Ihrer modifizierten Version von dotimes, wie definieren:

(defun foo (x) 
    (my-dotimes (var x) 
    (print "dotimes"))) 

und Sie nicht kompilieren.

Wenn Sie nur einmal (foo 5) aufrufen, muss der Interpreter das Makro vor dem Auswerten des erweiterten Codes erweitern, damit Sie die Ausdrucke sehen und langsamer als wenn Sie den erweiterten Code innerhalb der Funktion selbst geschrieben hätten.

Aber jetzt erscheint das Symbol my-times nicht mehr in der Definition von foo. Die Liste, in der sie enthalten war, wurde durch das Ergebnis ihrer Erweiterung ersetzt.

Also, wenn Sie wieder anrufen foo, wie (foo 3), jetzt sehen Sie nicht mehr die Drucke und es wird so schnell sein, als ob Sie selbst den erweiterten Code geschrieben hätte.

+0

Ja, ich habe es. – tom