26

我正在学习 Clojure 宏,代码示例有时会有结构'~symbol或交替~'symbol。我知道(quote'防止对表单进行评估,并且反引号还添加了命名空间限定,并且 ~ 导致对带引号的表单进行评估。我的问题是:为什么停止然后开始评估有用?我也假设~'symbol'~symbol是不同的,但怎么会呢?

4

2 回答 2

33

~'symbol用于产生不合格的符号。Clojure 的宏默认捕获命名空间,因此宏中的符号通常会解析为(your-namespace/symbol). unquote-quote 成语直接导致简单的、不合格的符号名称(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
于 2012-08-14T19:53:32.577 回答
6

~unquote函数的读取器宏。在带引号的列表中,它会导致评估符号而不是用作文字符号

user> (def unquoted 4)
user>`(this is an ~unquoted list)
(user/this user/is user/an 4 clojure.core/list)
user> 

除了 unquoted 符号之外的所有东西都被用作 unquoted 被解析为其值 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> 

在第二个示例中,我也没有使用 auto-gensyms 功能,因为我不在语法引用中

于 2012-08-14T19:12:07.240 回答