Comprendre les symboles non intransigeants et l'expansion des macros?

8

Je veux démontrer mon manque de connaissances avec un exemple.

En utilisant les deux définitions de macro suivantes,

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

la première macro masque toutes les variables nommées maxqui peuvent se produire dans le corps et la seconde non, ce qui peut être montré avec l'exemple suivant:

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

Pour autant que j'ai appris l'évaluation d'un macro appel fonctionne comme ceci:

La macro est évaluée deux fois. Le corps est d'abord évalué et retourne un formulaire. Ce formulaire est ensuite évalué à nouveau.

Jusqu'ici tout va bien.

Mais si je suppose que la macro renvoie vraiment un morceau de texte qui se trouve être une forme lisp, qui est ensuite interprétée, j'obtiens un conflit qui me rend incapable de comprendre l'exemple ci-dessus.

Quel morceau de texte la deuxième macro qui utilise make-symbolretourne-t-elle, afin qu'il n'y ait pas d'observation À mon avis, un nom de symbole choisi au hasard extrêmement improbable aurait du sens.

Si j'utilise les pp-macroexpand...deux macros, renvoyez la même extension.

Est-ce que quelqu'un peut m'aider à sortir de cette confusion?

clemera
la source
1
Ce n'est pas un morceau de texte, mais une liste de symboles. Certains peuvent être spéciaux et donc ne jamais entrer en collision avec d'autres ...
wasamasa
2
Si vous définissez print-gensymet print-circlepour tvous pourrez voir la différence dans les extensions de macro.
npostavs
@wasamasa comment ils sont spéciaux et ce qui fait que cela fonctionne, c'est exactement ce que je veux comprendre.
clemera
@npostavs Merci, donc le formulaire retourné est différent et ce n'est tout simplement pas visible avec les paramètres par défaut .... Je vois que le symbole non interne est remplacé par #:max. Qu'est-ce que ça veut dire ? Je suis très intéressé par plus de détails.
clemera
3
Le #:est juste une convention de l'imprimante pour indiquer un symbole uninterned (ce qui est ce qui print-gensymse met en marche), il est plus en détail dans le manuel: (elisp) Creating Symbols.
npostavs

Réponses:

4

Comme mentionné dans les commentaires, vous devez activer ce paramètre pour voir comment la macro-expansion fonctionne plus précisément. Notez que cela ne change que la façon dont macroexpandest affiché, les macros fonctionnent toujours de la même façon dans les deux cas:

(setq print-gensym t)

Ensuite, la première instance se développe en (incorrect):

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

Et le second se développe pour (corriger):

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

Pour mieux comprendre la différence, envisagez d'évaluer ceci:

(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

Comme vous pouvez le voir, le résultat de (make-symbol "max")n'a aucune valeur. Cela garantit l'exactitude de la première passe d'évaluation sur la macro. Avec la deuxième passe d' #:maxévaluation , gagne une valeur car elle est maintenant liée en tant que variable dynamique.

abo-abo
la source
Vous devez également print-circlesinon vous en avez 2 #:maxqui ne le sont pas eq(pensez aussi à imbriquer for).
npostavs
Merci, avec votre réponse et les commentaires, cela devient beaucoup plus clair pour moi. Mais le nom du symbole reste max non? Je suppose donc qu'il existe un moyen pour l'interprète de ne pas rechercher le symbole maxi non interne dans la table de symboles habituelle mais dans une autre table de recherche ...
clemera
Lors de la deuxième passe, le symbole max est une variable liée à la lettre: tout va bien dans ce cas. Dans le premier cas, il s'agit simplement d'un symbole unique et intime - il n'y a aucune tentative de l'interner. Jetez un œil cl-gensymsi ce n'est toujours pas clair.
abo-abo
Merci de votre aide. J'ai toujours des problèmes avec les détails: lors de la deuxième passe, il y a le let bound max et le #: max et je ne comprends toujours pas comment cela fonctionne qu'ils se distinguent. Si j'appelle symbol-name sur le make-symbol créé max, c'est max aussi. Alors pourquoi l'interprète n'est-il pas confus, quel est le mécanisme qui lui permet de rechercher la bonne valeur?
clemera