15

gensymClojureでは、マクロを衛生的に保つために、マクロで内部的に使用するシンボルを作成するためにを使用する必要があります。ただし、ネストされた構文引用符で同じ記号を使用する必要がある場合があります。たとえば、値をシンボルにバインドletし、展開されたループで3回出力する場合は、次のようにします。

`(let [x# 1]
   ~@(repeat 3
             `(println x#)))

しかし、それは

(clojure.core/let [x__2__auto__ 1]
                  (clojure.core/println x__1__auto__)
                  (clojure.core/println x__1__auto__)
                  (clojure.core/println x__1__auto__))

x#異なる構文引用符から作成されたため、フォーム内にネストされたフォームとは異なるシンボルをletフォームに生成します。println

それを解決するために、事前にシンボルを生成し、それを構文引用符に挿入することができます。

(let [x (gensym)]
  `(let [~x 1]
     ~@(repeat 3
               `(println ~x)))
) 

これにより、必要なすべての場所で同じ記号を使用して、正しい結果が生成されます。

(clojure.core/let [G__7 1]
                  (clojure.core/println G__7)
                  (clojure.core/println G__7)
                  (clojure.core/println G__7))

これで、正しい結果が得られますが、コード自体は醜く冗長に見えます。シンボルを「宣言」する必要はありません。インジェクション構文により、マクロの外部から来たか、マクロのどこかで計算されたように見えます。auto-gensym構文を使用できるようにしたいと思います。これにより、これらがマクロ内部シンボルであることを明確にできます。

それで、ネストされた構文引用符でauto-gensymを使用して、それらに同じシンボルを生成させる方法はありますか?

4

3 に答える 3

12

自動生成されたシンボルは、それらを定義する構文引用符内でのみ有効であり、構文引用符の一部ではないため、引用符のないコードでは機能しません。

ここで、シンボルx#は構文引用符の範囲内にあるため、その gensym に置き換えられます。

core> `(let [x# 1] x#)
(clojure.core/let [x__1942__auto__ 1] x__1942__auto__)

引用符を外すと、構文の引用符に変換されなくなります。

core> `(let [x# 1] ~@x#)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: x# in this context, compiling:(NO_SOURCE_PATH:1) 

auto-gensyms は、syntax-quote 内の非常に便利なショートカットです。それ以外gensymの場合は、後の例のように直接使用する必要があるようです。

このマクロを構成する方法は他にもありますが、autogensym が機能するようにしますが、マクロの先頭にある let で gensymed シンボルを宣言することは、Clojure やその他の Lisp でもごく普通のことです。

于 2012-10-01T16:04:00.833 に答える
9

あなたの方法(gensymを呼び出す)は正しいものです。

ただし、場合によってはdoto->またはを巧みに使用することで解決できます->>。見る:

 `(let [x# 1]
   (doto x#
     ~@(repeat 3 `println)))
于 2012-10-02T09:36:34.060 に答える