2016-04-04 22 views
2

xのinit()からend-1()のボディの値を1ずつ増やして計算するマクロ(my-dotimes [x init end] &本文)を書きたいと思います。 "可変キャプチャの問題"を回避する必要があります。各特定の変数にgensymを適用する方法

user=> (my-dotimes [x 0 4] (print x)) 
0123nil 

私のコードは次のとおりです:それはこのように動作するはず

(defmacro my-dotimes [[x initial end] & body] 
`(loop [i# ~initial] 
    (when (< i# ~end) 
     [email protected] 
     (recur (inc i#)))))) 

が、私はそれをチェックして検索しmacroexpandはを使用する場合:

user=> (macroexpand '(my-dotimes [x 0 4] (println x))) 
(loop* [i__4548__auto__ 0] (clojure.core/when (clojure.core/<i__4548__auto__ 4) 
(println x) 
(recur (clojure.core/inc i__4548__auto__)))) 

私は

を変更する方法を疑問に思って
(println x) => (clojure.core/println i__4548__auto__) 

答えて

4

ここでは、 gensymsを使用するには、には、が必要ですので、カウンタにバインドする必要のあるシンボル(ここではx)を使用してください。 i#を使用する代わりに、マクロのユーザーから与えられたシンボルを紹介してください。 新しいシンボルを導入するときにgensymsが必要で、既存のシンボルと衝突させたくない場合。

Common Lispでは、ユーザーのコードが反復の間にカウンタの値を変更する可能性があるため、の文字列を現在の値iにバインドして(let ((,x ,i)) ,@body)を使用して本文をラップすることは意味があります。悪くなる可能性があります)。しかし、ここでは変数を直接変更することはできないと思いますので、そのことについて心配する必要はありません。

あなたの第二の例は以下のとおりです。

(defmacro for-loop [[symb ini t change] & body] 
    `(loop [symb# ~ini] 
    (if ~t 
     [email protected] 
     (recur ~change)))) 

まず問題:あなたは、1つまたは複数の形式であるかもしれない体を展開するとき、あなたは終わりアップでしょう多くの枝の代わりに、2とifフォームで。体がx1,x2、の場合は、例えば(if test x1 x2 x3 (recur ...))となります。あなたは(do [email protected])doの式で本文を囲む必要があります。

状況はこれまでとほとんど変わらず、シンボルはユーザーによって与えられており、マクロ内でバインディングを設定する責任があります。新しいシンボルを作成するsymb#を使用する代わりに、symbから完全に区別され、symbを直接使用してください。 あなたが(未テスト)例えばこれを行うことができます:

(defmacro for-loop [[symb init test change] &body] 
    `(loop [~symb ~init] 
    (if ~test (do [email protected]) (recur ~change)))) 

を限り、あなたはあなたのマクロの呼び出し元が提供するシンボルを使用すると、gensymsは必要ありません。生成されたコードに新しい変数を作成する必要があるときは、gensymsが必要です。新鮮なシンボルが必要です。たとえば、式を1回だけ評価し、その値を保持する変数が必要です。

(defmacro dup [expr] 
    `(let [var# ~expr] 
     [var# var#])) 
+0

これはgensymを使用する必要はありません。しかし、ここでこの別の問題は、私はgensymを使用する必要があると思います:forループ(for-loop [symb init test change]&body])を書くと、変数、その初期値、終了条件、およびループの本体を指定します。 '(for-loop [i 0、(

+0

そして、私のコードでは、 '(forループのdefmacro [[SYMBのiniトン変更]&ボディ]' \t ''(ループ[symbは#〜のini] \t \t(あります(loop * [change])))) '' macroexpand 'を使用してコードをチェックすると、 'user =>(macroexpand'(forループ[i 0(

+2

@X iufenXu代わりに質問をコードブロックで更新してください。それは読みやすくなります。私もこれを見ています。 – coredump

関連する問題