12

Common Lisp について理解できないことがあります。

次のようなマクロを書いているとします。

(defmacro test-macro () 
   (let ((result (gensym))) 
      `(let ((,result 1))
         (print (incf ,result))))) 

私ができるよりも

> (test-macro)
2
2

これからどう展開していくのか見てみたい

> (macroexpand-1 '(test-macro))
(LET ((#:G4315 1)) (PRINT (INCF #:G4315))) ;
T

Ok。uninterned として印刷された gensym で生成された一意のシンボルがあります。

したがって、私が知る限り、インターンされていないシンボルは、エバリュエーターがシンボルとデータのバインディングを内部的に作成しないシンボルです。

したがって、その形式にマクロ展開すると、(incf #:G4315) でエラーが発生するはずです。これをテストするには、REPL でそのフォームを評価するだけです。

> (LET ((#:G4315 1)) (PRINT (INCF #:G4315)))
*** - SETQ: variable #:G4315 has no value

では、なぜこの文字列に展開するマクロは完全に機能し、フォーム自体は機能しないのでしょうか?

4

2 に答える 2

21

シンボルは、パッケージに含めることも、含めないこともできます。パッケージにインターンされたシンボルを検索して見つけることができます。パッケージ内でインターンされていないシンボルを検索することはできません。パッケージには、特定の名前のシンボルを 1 つだけ含めることができます。シンボルは 1 つだけCL-USER::FREDです。

あなたが書く

したがって、私が知る限り、インターンされていないシンボルは、エバリュエーターがシンボルとデータのバインディングを内部的に作成しないシンボルです。

それは間違っている。非インターンシンボルは、どのパッケージにもインターンされていないシンボルです。それ以外の場合は、完全に問題ありません。internedは、パッケージのレジストリにそのシンボルが登録されていることを意味します。

s-expressionリーダーは、読み取り中にシンボル名とパッケージを使用してシンボルを識別します。そのようなシンボルがない場合は、インターンされます。ある場合は、これが返されます。

リーダーは、現在のパッケージでシンボルを名前で検索します。

 (read-from-string "FOO") -> symbol `FOO`

二度目:

 (read-from-string "FOO") -> symbol `FOO`

常に同じ記号FOOです。

 (eq (read-from-string "FOO") (read-from-string "FOO"))  -> T

#:FOOという名前のインターンされていないシンボルの構文ですFOO。どのパッケージにもインターンされていません。リーダーがこの構文を見ると、インターンされていない新しいシンボルが作成されます。

 (read-from-string "#:FOO") -> new symbol `FOO`

二度目:

 (read-from-string "#:FOO") -> new symbol `FOO`

両方のシンボルは異なります。名前は同じですが、異なるデータ オブジェクトです。パッケージ以外にシンボル用のレジストリはありません。

 (eq (read-from-string "#:FOO") (read-from-string "#:FOO"))  -> NIL

したがって、あなたの場合(LET ((#:G4315 1)) (PRINT (INCF #:G4315)))、インターンされていないシンボルは異なるオブジェクトです。2 つ目は別の変数です。

Common Lisp にはデータを印刷する方法があるため、印刷/読み取り中に ID が保持されます

CL-USER 59 > (macroexpand-1 '(test-macro))
(LET ((#:G1996 1)) (PRINT (INCF #:G1996)))
T

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

CL-USER 61 > (macroexpand-1 '(test-macro))
(LET ((#1=#:G1998 1)) (PRINT (INCF #1#)))
T

#1=印刷された s 式には、最初のシンボルのラベルがあることがわかります。その後、同じ変数を参照します。これは読み返すことができ、シンボルの同一性は保持されます -リーダーがパッケージを見てシンボルを識別できなくても。

したがって、マクロは、1 つのシンボルのみが生成されるフォームを作成します。そのフォームを印刷して読み返したい場合、インターンされていないシンボルの ID が保持されていることを確認する必要があります。*print-circle*set toを使用して印刷するとT、これを行うのに役立ちます。

GENSYMQ: ( generate symbol )を使用して、インターンされていない生成済みシンボルをマクロで使用するのはなぜですか?

そうすれば、コード内の他のシンボルと衝突しない独自の新しいシンボルを作成できます。それらは関数によって名前を取得しますgensym-通常、最後に数えられた数があります。これらはどのパッケージにもインターンされていない新しいシンボルであるため、名前の競合は発生しません。

CL-USER 66 > (gensym)
#:G1999

CL-USER 67 > (gensym)
#:G2000

CL-USER 68 > (gensym "VAR")
#:VAR2001

CL-USER 69 > (gensym "PERSON")
#:PERSON2002

CL-USER 70 > (gensym)
#:G2003

CL-USER 71 > (describe *)

#:G2003 is a SYMBOL
NAME          "G2003"
VALUE         #<unbound value>
FUNCTION      #<unbound function>
PLIST         NIL
PACKAGE       NIL                      <------- no package
于 2012-12-21T09:35:30.877 に答える
0

gensymシンボルを生成し、それを印刷すると、そのシンボルの「文字列」表現が得られますが、これは「リーダー」表現、つまりシンボルのコード表現とは異なります。

于 2012-12-21T09:35:04.093 に答える