Nicht-interne Symbole und Makro-Erweiterung verstehen?


8

Ich möchte meinen Mangel an Wissen anhand eines Beispiels demonstrieren.

Verwenden Sie die folgenden zwei Makrodefinitionen:

(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 'max))
    `(let ((,var ,init)
           (,tempvar ,final))
       (while (<= ,var ,tempvar)
         ,@body
         (setq ,var (1+ ,var))))))
(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
         (setq ,var (1+ ,var))))))

Das erste Makro schattiert alle benannten Variablen, maxdie im Körper auftreten können, und das zweite nicht, was anhand des folgenden Beispiels gezeigt werden kann:

(let ((max 0)) ;; this variable is shadowed if the first macro defintion is used
  (for i from 1 to 3 do
       (setq square (* i i))
       (if (> square max) 
         (princ (format "\n%d %d" i square)))))

Soweit ich erfahren habe, funktioniert die Auswertung eines Makroaufrufs folgendermaßen:

Das Makro wird zweimal ausgewertet. Zuerst wird der Körper ausgewertet und gibt ein Formular zurück. Dieses Formular wird dann erneut ausgewertet.

So weit, ist es gut.

Wenn ich jedoch davon ausgehe, dass das Makro wirklich einen Textblock zurückgibt, der zufällig eine Lisp-Form ist und dann interpretiert wird, entsteht ein Konflikt, der mich dazu bringt, das obige Beispiel nicht zu verstehen.

Welchen Textblock gibt das zweite Makro, das make-symbolreturn verwendet, so dass keine Schattenbildung auftritt? Nach meinem Verständnis wäre ein extrem unwahrscheinlicher, zufällig gewählter Symbolname sinnvoll.

Wenn ich pp-macroexpand...beide Makros verwende, wird dieselbe Erweiterung zurückgegeben.

Kann mir jemand aus dieser Verwirrung helfen?


1
Es ist kein Textblock, sondern eine Liste von Symbolen. Einige davon können etwas Besonderes sein und kollidieren daher nie mit anderen ...
Wasamasa

2
Wenn Sie print-gensymund print-circleauf setzen , können tSie den Unterschied in den Makroerweiterungen sehen.
npostavs

@wasamasa wie sie besonders sind und warum dies tatsächlich funktioniert, ist genau das, was ich verstehen möchte.
Clemera

@npostavs Danke, also ist das zurückgegebene Formular anders und dies ist mit den Standardeinstellungen einfach nicht sichtbar. Ich sehe, dass das nicht interinternierte Symbol durch ersetzt wird #:max. Was bedeutet das ? Ich bin sehr an weiteren Details interessiert.
Clemera

3
Das #:ist nur eine Konvention des Druckers ein uninterned Symbol , um anzuzeigen , (das ist , was print-gensymantörnt), gibt es mehr ausführlich im Handbuch: (elisp) Creating Symbols.
Npostavs

Antworten:


4

Wie in den Kommentaren erwähnt, müssen Sie diese Einstellung aktivieren, um zu sehen, wie die Makroerweiterung genauer funktioniert. Beachten Sie, dass sich nur die macroexpandAnzeige ändert . Die Makros funktionieren in beiden Fällen immer noch gleich:

(setq print-gensym t)

Dann wird die erste Instanz auf (falsch) erweitert:

(let ((max 0))
  (let ((i 1)
        (max 3))
    (while (<= i max)
      (setq square (* i i))
      (if (> square max)
          (princ
           (format "\n%d %d" i square)))
      (setq i (1+ i)))))

Und der zweite erweitert sich zu (richtig):

(let ((max 0))
  (let
      ((i 1)
       (#:max 3))
    (while
        (<= i #:max)
      (setq square
            (* i i))
      (if
          (> square max)
          (princ
           (format "\n%d %d" i square)))
      (setq i
            (1+ i)))))

Um den Unterschied besser zu verstehen, sollten Sie Folgendes bewerten:

(setq max 10)
;; => 10
(symbol-value 'max)
;; => 10
(symbol-value (make-symbol "max"))
;; => Caught unbound variable #:max, setting it to nil.
(make-symbol "max")
;; => #:max
(eq (make-symbol "max")
    (make-symbol "max"))
;; => nil

Wie Sie sehen können, hat das Ergebnis von (make-symbol "max")keinen Wert. Dies stellt die Richtigkeit des ersten Auswertungsdurchlaufs des Makros sicher. Erhält mit dem zweiten Auswertungsdurchgang #:maxeinen Wert, da er jetzt als dynamische Variable gebunden ist.


Sie müssen auch print-circlesonst 2 haben #:max, die nicht sind eq(betrachten Sie auch verschachtelt for).
npostavs

Vielen Dank, mit Ihrer Antwort und den Kommentaren wird es mir viel klarer. Aber der Symbolname bleibt maximal richtig? Ich gehe also davon aus, dass es eine Möglichkeit gibt, dass der Interpreter nicht das nicht internierte Symbol max in der üblichen Symboltabelle, sondern in einer anderen Nachschlagetabelle
nachschlagen kann

Beim zweiten Durchgang ist das Symbol max eine let-gebundene Variable: In diesem Fall ist alles in Ordnung. Im ersten Fall handelt es sich nur um ein eindeutiges nicht interniertes Symbol - es gibt keinen Versuch, es zu internieren. Schauen Sie sich an, cl-gensymob es noch nicht klar ist.
abo-abo

Danke für Ihre Hilfe. Ich habe immer noch Probleme mit den Details: Beim zweiten Durchgang gibt es das let bound max und das #: max und ich verstehe immer noch nicht, wie es funktioniert, dass sie unterscheidbar sind. Wenn ich den Symbolnamen für das maximal erstellte Make-Symbol aufrufe, ist es auch max. Warum ist der Dolmetscher nicht verwirrt? Welchen Mechanismus kann er nach dem richtigen Wert suchen?
Clemera
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.