Clojure中的'或~有什么作用?

26

我正在学习Clojure宏,有些代码示例中会出现'~symbol~'symbol。我知道(quote'可以阻止表达式的求值,并且反引号(backquote)还会添加命名空间限定,而波浪线(~)会导致一个引用的表单被求值。我的问题是:停止再开始求值有什么用处?我还假设~'symbol'~symbol是不同的,但具体有何不同呢?

2个回答

33

~'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 (quote user/foo)) 的结果是 foo 而不是 user/foo,什么时候以及如何发生命名空间被剥离的情况?这与 unquote 的“正常”行为如何保持一致,即对以下形式进行求值? - skuro
1
第一个例子不起作用。 应该是 \(let [~'it ~expr] ...` - Ming
@skuro 逐步操作如下:1. `foo 捕获了命名空间,因此评估为 user/foo。2. `'foo 命名空间仍然被捕获,但结果是对引用的调用:(quote user/foo)。3. `~'foo 仍然被捕获,然后被包装在引号中,最后引号被评估,返回只有 foo。同样的方式 '+ 评估为 +。 - xixixao

6

~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> 

在第二个示例中,我也没有使用自动生成符号的功能,因为我不在语法引用中。

我知道为什么我想在之前引用的列表中,在符号前使用,但我的问题是为什么我会使用'或'~紧挨着彼此。 - Alex
希望这个例子能更好地展示动机,感谢反馈 :) - Arthur Ulfeldt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接