2012-02-23 12 views
9

概念レベルでは、LISP(および方言)のマクロは(リストとして)コードを取り、別のコードを(リストとして)返します。マクロでのリストとバックティックの違い

(defmacro zz [a] (list print a)) 
;macroexpand says : (#<core$print [email protected]> "Hello") 

しかし、Clojureの中で、これはまた、以下のように書くことができる:

(defmacro zz [a] `(print ~a)) 
;macroexpand says : (clojure.core/print "Hello") 

私はここでの違いについて正確にわからないとどのべき簡単なマクロは可能性の原則の上に基づいて

好ましい方法である。最初のものは単純に見えますが、リストを返すようになっていて、バックティックのような奇妙な文字の使用は避けてください。

答えて

8

は、誰がこれを指摘していない、まだ...あなたの2つのマクロの違いは、これがある:あなたの第二の形式(使用してバッククォート)あなたの最初の例と異なっている

(defmacro zz [a] (list 'print a)) 

(defmacro zz [a] `(print ~a)) 

は同等です:

(defmacro zz [a] (list print a)) 

マクロクォックスが異なる理由は、一重引用符がないことに注意してください。私は他の人が投稿していることに同意します:もしあなたのマクロがかなりシンプルな '形'を持っていれば、バッククォートの使用はより一般的です。コードウォーキングや動的構築(つまり複雑なマクロ)を行う必要がある場合は、リストを使用してビルドを行うことがよくあります。

この説明が意味をなされることを望みます。

+1

これは実際には '(list \' print a) 'と似ていますが、' 'print''はローカルではなく' 'clojure.core/print''を参照して'または全くバインドしない(名前空間の ':refer-clojure'から除外したとします)。一方、 '\' print'は 'clojure.core/print'に直接展開されるので、どんな文脈でも曖昧ではなく正しいです。 – amalloy

+0

@amalloyご回答いただきありがとうございます。ご回答いただきありがとうございます。私はちょうど '' '(print〜a)'のマクロ展開を(スライム経由で)しようとしました。 )) ' - 'ああ、しかし、あなたは、変換前のフォームが '' 'print''を持つことに似ていると言っていますか? –

+0

SOがバックティックキャラクターの一部を食べ​​ているので、私はあなたのレスポンスをうまく読み取れませんが、スライムはネームスペースを隠しています(そしてマクロエクスパンドは必要ありません)。式に '' \ '(print〜a)'と入力するだけで式を引用すれば、 '' clojure.core/seq(clojure.core/concat(clojure.core/list(quote clojure.core/print))(clojure.core/list a))) 'を実行します。 'clojure.core/print'は重要な違いです。 – amalloy

1

私の経験では同等です。私は気づいていないいくつかのエッジケースがあるかもしれませんが。

@islonの例は、同等のように書くことができます:コントラスト

、上記のコードは、等価的にそうように書くことができます。

(defmacro unless [condition & body] 
    (list 'if (list 'not condition) 
      (list* 'do body))) 
5

それらの間のスタイルの違いがあります。あなたの例は非常にシンプルですが、より複雑なマクロでは違いが大きくなります。本から

(defmacro unless [condition & body] 
    `(if (not ~condition) 
     (do [email protected]))) 

構文引用符があれば、フォーム、次のように動作させることができますブック「のClojureの喜び」に定義されている例えば

ない限り、マクロ式 のテンプレートの一種で、マクロを展開するときにそのマクロが使用されるようになりました。

マクロを作成するときは、常に最も読みやすく慣用的なスタイルを選択します。コントラスト

は、上記のコードは、等価的にそうように書くことができます。

(defmacro unless [condition & body] 
    (list 'if (list 'not condition) 
      (list* 'do body))) 
6

リストを明示的に作成することは、あなたが知る必要があるコア概念がほとんどないため、「簡単」です。リストを受け入れ、新しいリストがあるまでそれを変更するだけです。 Backtickは、コードの断片を「テンプレート化する」ための便利なショートカットです。それを使わないでマクロを書き込むことは可能ですが、大きなマクロではすぐに非常に不快になります。例えば、fn以上のマクロとしてletを書くの二つの方法を検討:

(defmacro let [bindings & body] 
    (let [names (take-nth 2 bindings) 
     vals (take-nth 2 (rest bindings))] 
    `((fn [[email protected]] 
     (do [email protected])) 
     [email protected]))) 

(defmacro let [bindings & body] 
    (let [names (take-nth 2 bindings) 
     vals (take-nth 2 (rest bindings))] 
    (cons (list `fn (vec names) (cons `do body)) 
      vals))) 

最初のケースでは、バッククォートを使用することで、体を含む名前の関数を記述して、呼び出していること、それはかなり明確になりますマクロコードは拡張コードと同じ形になっているので、どのように見えるか想像することができます。

2番目のケースでは、ちょうどconslistとなっています。拡張がどのように見えるかは、実際の頭痛です。これは必ずしも当てはまるわけではありません。時には、バックティックなしで何かを書くことが明確になることがあります。

別の非常に重要な点は、カイルバートンによって行われました。print'printと同じではありません!マクロ展開には、その値(関数)ではなく、シンボルprintが含まれている必要があります。コードにオブジェクト(関数など)を埋め込むことは非常に壊れやすく、偶然にしか機能しません。したがって、実際に自分で書いたコードにマクロが展開されていることを確認してから、評価システムにハードワークをさせてください。シンボルprintをタイプすることができますが、関数の現在の値へのポインタは入力できません。print

関連する問題