非インターンシンボルとマクロ展開を理解していますか?


8

私の知識不足を例を挙げて説明したいと思います。

次の2つのマクロ定義を使用して、

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

最初のマクロmaxは、本体で発生する可能性がある名前付きの変数をシャドウし、2番目のマクロはシャドウしません。これは、次の例で示すことができます。

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

私が学んだ限り、マクロ呼び出しの評価は次のように機能します。

マクロは2回評価されます。最初に本体が評価され、フォームを返します。その後、このフォームは再度評価されます。

ここまでは順調ですね。

しかし、マクロが実際にlispフォームであるテキストのチャンクを返し、それが解釈されると仮定すると、上記の例を理解できなくなる競合が発生します。

make-symbolシャドウイングが発生しないように、使用する2番目のマクロが返すテキストのチャンクは何ですか?私の理解では、非常にありそうにないランダムに選択されたシンボル名が理にかなっています。

pp-macroexpand...両方のマクロを使用すると、同じ展開が返されます。

この混乱から誰かが私を助けることができますか?


1
これはテキストのチャンクではなく、シンボルのリストです。一部は特別であり、したがって他の人と衝突することはありません...
wasamasa

2
あなたが設定した場合print-gensymprint-circlet、あなたはマクロ展開の違いを見ることができます。
npostavs

@wasamasaそれらがどのように特別であり、これが実際に機能するかはまさに私が理解したいものです。
クレメラ

@npostavsおかげで、返されたフォームは異なり、これはデフォルト設定では表示されません。...非抑留シンボルはに置き換えられ#:maxます。どういう意味ですか ?もっと詳しく知りたいです。
クレメラ

3
#:ちょうどインターンのシンボルを示すために、プリンタの大会(これは何であるprint-gensymオンにする)、マニュアルの詳細があります:(elisp) Creating Symbols
npostavs 2015

回答:


4

コメントで述べたように、マクロ展開がより正確に機能する方法を確認するには、この設定をオンにする必要があります。macroexpand表示方法が変わるだけで、マクロはどちらの場合でも同じように機能します。

(setq print-gensym t)

次に、最初のインスタンスが(不正)に展開されます。

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

そして、2つ目は(正しい)に展開されます。

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

違いをさらに理解するには、これを評価することを検討してください:

(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

ご覧のとおり、の結果に(make-symbol "max")は価値がありません。これにより、マクロの最初の評価パスの正確さが保証されます。2番目の評価パスで#:maxは、動的変数としてバインドされているため、値を取得します。


また、print-circleそうでない場合は2 #:maxでないことも必要ですeq(nestedも考慮してくださいfor)。
npostavs

おかげで、あなたの答えとコメントで、それが私にははるかに明確になります。しかし、シンボル名は最大のままです?そのため、通常のシンボルテーブルではなく、別のルックアップテーブルでインターンされていないシンボルマックスをルックアップしないようにインタープリターが知っている方法があると思います...
clemera

2番目のパスでは、シンボルmaxはlet-bound変数です。その場合はすべて問題ありません。最初のケースでは、それは単なる非インターン化されたシンボルです-それをインターンする試みはありません。cl-gensymそれがまだはっきりしていないかどうか見てください。
abo-abo

ご協力いただきありがとうございます。私はまだ詳細に問題があります:2番目のパスでは、let bound maxと#:maxがあり、それがどのように機能するのかがわかりません。make-symbolで作成されたmax-symbolでsymbol-nameを呼び出すと、それもmaxになります。それでは、なぜインタープリターは混乱しないのですか?正しい値を検索する方法を彼が知っているメカニズムは何ですか?
クレメラ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.