我正在学习Clojure宏,有些代码示例中会出现'~symbol
或~'symbol
。我知道(quote
和'
可以阻止表达式的求值,并且反引号(backquote)还会添加命名空间限定,而波浪线(~)会导致一个引用的表单被求值。我的问题是:停止再开始求值有什么用处?我还假设~'symbol
和'~symbol
是不同的,但具体有何不同呢?
~'symbol
用来生成一个不带限定名称的符号。Clojure的宏默认情况下会捕获命名空间,所以宏中的符号通常会被解析为 (your-namespace/symbol)
。非引用引用习惯直接通过评估引用符号来产生简单的、不带限定名称的符号名 - (symbol)
。来自《Clojure的欢乐》:
(defmacro awhen [expr & body]
`(let [~'it ~expr] ; refer to the expression as "it" inside the body
(when ~'it
(do ~@body))))
(awhen [:a :b :c] (second it)) ; :b
'~symbol
可能用于在宏中插入名称或类似内容。在这里,symbol
将绑定到一个值 - let [symbol 'my-symbol]
。然后通过评估 symbol
,将该值插入宏产生的代码中。
(defmacro def-symbol-print [sym]
`(defn ~(symbol (str "print-" sym)) []
(println '~sym))) ; print the symbol name passed to the macro
(def-symbol-print foo)
(print-foo) ; foo
~
是unquote
函数的读取宏。在引用列表中,它导致符号被评估而不是作为文字符号使用。
user> (def unquoted 4)
user>`(this is an ~unquoted list)
(user/this user/is user/an 4 clojure.core/list)
user>
除去未加引号的符号被解析为4之外,其他所有东西都只是作为一个符号使用。这在编写宏时最常用。当repl打印出结果列表时,它也会在名称前面打印命名空间(用户)。
许多宏基本上只是模板,旨在对无法在函数中完成的一些事情进行一堆轻微变化。在这个人为的例子中,模板宏通过产生对def的调用来定义函数。语法引用和去引用使这更容易阅读:
user> (defmacro def-map-reducer [name mapper reducer]
`(defn ~name [& args#]
(reduce ~reducer (map ~mapper args#))))
#'user/def-map-reducer
user> (def-map-reducer add-incs inc +)
#'user/add-incs
user> (add-incs 1 2 3 4 5)
20
与之相比:
user> (defmacro def-map-reducer [name mapper reducer]
(let [args-name (gensym)]
(list `defn name [`& args-name]
(list `reduce reducer (list `map mapper args-name)))))
#'user/def-map-reducer
user> (def-map-reducer add-decs dec +)
#'user/add-decs
user> (add-decs 1 2 3 4 5)
10
user>
(unquote (quote user/foo))
的结果是foo
而不是user/foo
,什么时候以及如何发生命名空间被剥离的情况?这与unquote
的“正常”行为如何保持一致,即对以下形式进行求值? - skuro\
(let [~'it ~expr] ...` - Ming