2016-06-23 13 views
3

(謝罪これは別の質問の重複している場合、それらすべての空想の特殊文字のための私の検索では何も得られませんでした。)なぜこのクロージャーマクロは `〜〜を必要としますか?

私はMastering Clojure Macrosを読んでいると、次の例を理解悩みを持っている:

(defmacro inspect-caller-locals [] 
    (->> (keys &env) 
     (map (fn [k] [`'~k k])) 
     (into {}))) 
=> #'user/inspect-caller-locals 
(let [foo "bar" baz "quux"] 
    (inspect-caller-locals)) 
=> {foo "bar", baz "quux"} 

以下との違いは何ですか?'k

`'~k 

私の知る限り理解し、最も内側にunquote ~は、単に最も外側の構文引用符 `の効果を元に戻し必要がありますが、短い実験はそれによりがあることを明らかに:、残念ながら

(defmacro inspect-caller-locals-simple [] 
    (->> (keys &env) 
     (map (fn [k] ['k k])) 
     (into {}))) 
=> #'user/inspect-caller-locals-simple 
(let [foo "bar" baz "quux"] 
    (inspect-caller-locals-simple)) 
CompilerException java.lang.RuntimeException: Unable to resolve symbol: k in this context, compiling:(/tmp/form-init4400591386630133028.clj:2:3) 

私の通常の調査アプローチはここには適用されません:

(macroexpand '(let [foo "bar" baz "quux"] 
       (inspect-caller-locals))) 
=> (let* [foo "bar" baz "quux"] (inspect-caller-locals)) 
(let [foo "bar" baz "quux"] 
    (macroexpand '(inspect-caller-locals))) 
=> {} 

私はここで何が欠けていますか?

+3

macroexpandの代わりに、あなたが気にするマクロの部分を評価してください。 : '(let [k 'foo] [k' k \' '〜k]) ' – amalloy

答えて

4

てみましょう最初のマクロ内部kが何であるかを確立します。関数の内部だから、

(defmacro inspect-caller-locals [] 
    (mapv (fn [k] 
      (println (class k))) 
     (keys &env)) 
    nil) 
(let [x 1] 
    (inspect-caller-locals)) 
;; Prints: 
;; clojure.lang.Symbol 

あなたは、各kシンボルです。マクロからシンボルを返すと(つまりコードを生成すると)、clojureはそれが参照する値を検索して出力します。たとえば、次のようにします。

(defmacro inspect-caller-locals [] 
    (mapv (fn [k] 
      [(quote x) k]) ;; not the "hard coded" x 
     (keys &env))) 
(let [x 1] 
    (inspect-caller-locals)) 
;; Prints: 
;; [[1 1]] 

実際のシンボルは何ですか?問題は(あなたが指摘したように)quoteは、あなたがそれを何とか評価していない特別なフォームです。すなわち、kは関数のパラメータを得るが、通常は定義されていないk滞在しないだろう。

(defmacro inspect-caller-locals [] 
    (mapv (fn [k] 
      [(quote k) k]) 
     (keys &env))) 
(let [x 1] 
    (inspect-caller-locals)) 
;; => Error 
(let [k 1] 
    (inspect-caller-locals)) 
;; Prints: 
;; [[1 1]] 

あなたは何とかあなたがquoteに渡すものを評価する必要があることがquoteが何をするかではないので、これはしかし、ことはできません。このようstrなどの他の機能は、その問題を持っていない:

(defmacro inspect-caller-locals [] 
    (mapv (fn [k] 
      [(str k) k]) 
     (keys &env))) 
(let [x 1] 
    (inspect-caller-locals)) 
;; Prints: 
;; [["x" 1]] 

トリックが深い1つのレベルに行くと、あなたがそれにシンボルを渡すことができるようにquote自体を引用することです:

(defmacro inspect-caller-locals [] 
    (mapv (fn [k] 
      [;; This will evaluate k first but then generate code that 
      ;; wraps that symbol with a quote: 
      (list (quote quote) k) 
      ;; Or equivalently and maybe easier to understand: 
      (list 'quote k) 
      k]) 
     (keys &env))) 
(let [x 1] 
    (inspect-caller-locals)) 
;; Prints: 
;; [[x x 1]] 

かリーダーを使って、あなたのためにこれを行うことができます。

(defmacro inspect-caller-locals [] 
    (mapv (fn [k] 
      [`(quote ~k) 
      `'~k 
      k]) 
     (keys &env))) 
(let [x 1] 
    (inspect-caller-locals)) 
;; Prints: 
;; [[x x 1]] 

後にすべてのため:

(read-string "`'~k") 
=> (clojure.core/seq (clojure.core/concat (clojure.core/list (quote quote)) (clojure.core/list k))) 
(defmacro inspect-caller-locals [] 
    (mapv (fn [k] 
      [(clojure.core/seq (clojure.core/concat (clojure.core/list (quote quote)) (clojure.core/list k))) 
      k]) 
     (keys &env))) 
(let [x 1] 
    (inspect-caller-locals)) 
;; Prints: 
;; [[x 1]] 
1

いくつかの代替、および同等の、書き込み

`'~k

の方法があります。

`(quote ~k) ;; expands the ' reader macro to the quote special form

(list 'quote k) ;; avoids syntax quote entirely

は、あなたが考えることはかなり右であること

最も内側にunquoteは〜すべきでは単に

あなたの説明から欠落している唯一のこと、あなたがするので、構文引用符で囲まれた式のquote外を引くことができないことがあり、最も外側の構文引用符の効果を元に戻しますquoteは特殊な形式であり、内部の意味を変更します。そうでない場合、 '`~k 'kに相当し、気づいたように、そうではありません。私はREPL、マクロ/マクロ展開の文脈の外に構文引用されたものをしようと、これらのものの周りにあなたの頭を取得するための最良の方法であることは、@ amalloyの一般的なアドバイスをエコーう

p.s.また、私はこの混乱を修正する必要があることに注意してください。

関連する問題