2016-07-08 18 views
1

ich berühre elisp vor kurzem und versuche zu verstehen, wie elisp makro funktioniert. Das GNU-Tutorial hat eine chapter Surprising-local-Vars für Makro lokale Variable und ich bin verwirrt darüber, wie die Makro-Erweiterung funktioniert.elisp makroexpansion lokale variable

(defmacro for (var from init to final do &rest body) 
    "Execute a simple for loop: (for i from 1 to 10 do (print i))." 
    (let ((tempvar (make-symbol "max"))) 
    `(let ((,var ,init) 
      (,tempvar ,final)) 
     (while (<= ,var ,tempvar) 
     ,@body 
     (inc ,var))))) 

Es gibt zwei Let-Formen. die ersten

(let ((tempvar (make-symbol "max"))) 

hat hat nicht das Backquote, die auf dem Makro Ausdruck erweitert ausgewertet wird erhalten, dafür das uninterned Symbol „max“ wird nur in diesem Satz erstellt bekommen. Und das nicht signalisierte Symbol "max" wird zur Laufzeit verloren gehen, sollte es nicht richtig funktionieren?

Aber eigentlich funktioniert es gut. Ich habe versucht, die folgenden:

(for max from 1 to 10 do (print max)) 

Und die seine Expansion, wie folgend:

(macroexpand '(for max from 1 to 10 do (print max))) 

(let ((max 1) (max 10)) (while (<= max max) (print max) (setq max (+ 1 max)))) 

zwei max Symbol hier, eine gebunden zu 1, eine weitere Schranke bis 10, hat die während Form Ausdruck zwei max Symbol.

wie löst die while-form die zwei verschiedenen das symbol "max" auf?

Antwort

3

Sie betrachten Drucknamen, nicht Symbolidentitäten. Sie haben zwei Symbole, beide mit dem "Drucknamen" max, aber unterschiedlichen Identitäten. Normalerweise würde ich gensym anstelle von make-symbol empfehlen, aber es spielt wirklich keine große Rolle, wie es gemacht wird.

Stellen Sie sich ein Symbol als einen Zeiger auf eine kleine Struktur vor, in der eine Vielzahl von Werten gespeichert ist. Einer davon ist ein "Name", wenn ein Symbol interniert ist, wird es in einer speziellen Struktur platziert, so dass Sie das Symbol durch seinen Namen finden können. Was Sie sehen, ist ein interniertes Symbol mit dem Namen max und ein nicht interniertes Symbol mit dem Namen max. Sie sind verschiedene Symbole (das heißt, zwei Strukturen und die Zeiger sind also unterschiedlich), aber wenn Sie nur eine gedruckte Darstellung betrachten, ist dies nicht offensichtlich.

Eine schnelle Demonstration, frisch aus einem Emacs „kratzen“ Puffer:

(defmacro demo (sym) 
    (let ((tmp (make-symbol "max"))) 
    `(progn 
     (message "%s" (list ',tmp ',sym)) 
     (eql ',tmp ',sym)))) 

demo 

(macroexpand '(demo max)) 
(progn (message "%s" (list (quote max) (quote max))) (eql (quote max) (quote max))) 

(demo max) 
nil 

Wenn Sie den Text einfügen, die von der Makro-Erweiterung führt und auswerten, werden Sie sehen, dass Sie t erhalten anstelle von nil, weil während des Lesens des Ausdrucks Sie mit dem selben Symbol enden.

+0

Sinn machen. Und für die erste Frage, könnten Sie mehr darüber erklären? Die erste Let-Form hat nicht das Backquote, daher wird sie bei expand phrase ausgewertet (das nicht-internierte Symbol wird zu diesem Zeitpunkt erstellt.) Und das erstellte nicht-internierte Symbol wird wahrscheinlich zur Laufzeit nicht existieren der fall: kompilieren sie den elisp code, das macro wird erweitert, das nicht-internierte symbol "max" wird erstellt, beim nächsten mal direkt aus dem byte code, das nicht-internierte symbol "max" existiert nicht, wie geht das Arbeit?). –

+0

Das nicht unterbundene Symbol wird (zumindest) solange leben, wie Referenzen darauf vorhanden sind. Der Makro-Expanded-Code hat einen Verweis auf das nicht-internierte Symbol. Sie erhalten jedes Mal, wenn das Makro expandiert wird, in der Erweiterung ein * anderes * Symbol ohne Unterbrechung. "Uninterned" bedeutet nur und genau, dass Sie das Symbol nicht mit seinem Namen finden können, sondern nur, indem Sie bereits einen Verweis darauf haben. – Vatine

+1

In Common Lisp würde das Setzen von Druckkreis auf T im Allgemeinen Symbole mit demselben Namen mit Leservariablen drucken. Das scheint bei Emacs nicht der Fall zu sein. – coredump

1

Die kurze Antwort ist, dass (make-gensym "max") erstellt ein neues Symbol, dessen Name max. Sie verwenden auch ein Symbol, dessen Name max ist. Diese haben den gleichen Namen und somit in Emacs, Drucken in der gleichen Weise, aber sie sind nicht das gleiche Symbol.Wir können dies leicht testen, indem Sie ein Makro erstellen, die ein Symbol nimmt erzeugt und gibt eine Form, die es mit dem Makro-Argument vergleicht:

(defmacro test-gensym (arg) 
    (let ((max (make-symbol "max"))) 
    `(eq ',max ',arg))) 

Wenn wir die macroexpansion betrachten, können wir sehen, dass wir zwei Symbole sind zu vergleichen deren Namen sind die gleichen:

(print (macroexpand '(test-gensym max))) 
;;=> (eq (quote max) (quote max)) 

Aber wenn wir tatsächlich diesen Code ausführen, werden wir sehen, dass die Werte nicht gleich sind verglichen werden:

(test-gensym max) 
;;=> nil 
0

Zum Vergleich mit Common Lisp: Hier sehen Sie, dass der Common Lisp Drucker die Variable unterschiedlich aussehen lässt. Einer ist Ihr max und der andere ist ein nicht untergebrachter #:max.

CL-USER 70 > (pprint (macroexpand '(for max from 1 to 10 do (print max)))) 

(LET ((MAX 1) (#:MAX 10)) 
    (LOOP WHILE (<= MAX #:MAX) DO (PROGN (PRINT MAX) (INCF MAX)))) 

Wenn wir die Common Lisp Drucker anweisen Kreisdatenstrukturen zu unterstützen, drucken, dann werden die Druckmarken, wo die uninterned Symbole sind die gleichen: #1=#:MAX das Symbol mit einem Drucker Label. #1# ist dann ein Verweis auf dieses etikettierte Ding.

CL-USER 71 > (setf *print-circle* t) 
T 

CL-USER 72 > (pprint (macroexpand '(for max from 1 to 10 do (print max)))) 

(LET ((MAX 1) (#1=#:MAX 10)) 
    (LOOP WHILE (<= MAX #1#) DO (PROGN (PRINT MAX) (INCF MAX)))) 

Aber das ist Common Lisp, nicht Emacs Lisp - obwohl sie verwandt sind, durch beiden Nachfolger zu einem frühen Lisp-Dialekt zu sein.