Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么此clojure宏需要`';~?_Clojure_Macros_Quote - Fatal编程技术网

为什么此clojure宏需要`';~?

为什么此clojure宏需要`';~?,clojure,macros,quote,Clojure,Macros,Quote,(很抱歉,如果这是另一个问题的重复,我对所有这些奇特的特殊角色的搜索没有产生任何结果。) 我正在阅读,无法理解以下示例: (defmacro inspect-caller-locals [] (->> (keys &env) (map (fn [k] [`'~k k])) (into {}))) => #'user/inspect-caller-locals (let [foo "bar" baz "quux"] (inspect-c

(很抱歉,如果这是另一个问题的重复,我对所有这些奇特的特殊角色的搜索没有产生任何结果。)

我正在阅读,无法理解以下示例:

(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
~
应该简单地恢复最外面的语法quote`的效果,但一个简短的实验表明,它有更多的含义:

(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)))
=> {}

我在这里遗漏了什么?

让我们首先确定宏中的
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]]
诀窍是更深一层,并引用
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]]

一些可供选择的、同等的写作方式


`“~k

它们是:


`(引用~k);;将“reader”宏扩展为quote特殊表单


(列表"k);;;完全避免语法引用

你这样想是对的

最里面的unquote~应该只是还原最外面的语法quote的效果

您的描述中唯一缺少的是,您不能将
quote
拉到语法引用表达式之外,因为
quote
是一种特殊形式,它改变了内部内容的含义。否则,,

“`k
相当于
'k
——正如您所注意到的,它不是

我将附和@amalloy的一般建议,即在REPL中尝试语法引用的东西,在宏/宏扩展的上下文之外,是让您的头脑了解这些东西的最佳方式


p、 另外,我会注意到,我需要通过在未来的图书版本中更好地解释来解决这一困惑;)

与macroexpand不同的是,在repl:
(让[k'foo][k'k`''~k])中运行它,尝试只评估您关心的宏部分。