Clojure宏:引用和语法引用
假设我有以下代码:
(defmacro test1 [x]
  (list 'fn '[y]
        (if (pos? x)
          '(println y)
          '(println (- y)))))
  它做我需要的,根据x编写一个函数,并且不会引用x。  例如, (test1 1)宏展开为(fn* ([y] (println y))) 。 
现在,我想用语法引用来重写它。 这是我到目前为止:
(defmacro test2 [x]
  `(fn [y#]
     (if ~(pos? x)
       (println y#)
       (println (- y#)))))
  这完全一样,只有一个例外:它在展开的表达式中留下了一个(if true ..)表达式: 
 (fn* ([y__12353__auto__] (if true (clojure.core/println y__12353__auto__) (clojure.core/println (clojure.core/- y__12353__auto__))))) 
如果编译器可以优化它,这可能不是问题。 不过,有没有办法可以省略它?
  当你使用test2 ,它将取消引用整个表单(pos? x) ,它将在编译时工作,如果它是一个常数或可能已经定义的gloabl,但是如果你传递一个不存在的词汇范围变量名然而。 
因此,你真的想要这个:
(defmacro test2 [x]
  `(fn [y#]
     (if (pos? ~x) ; just unquote x, not the whole predicate expression
       (println y#)
       (println (- y#)))))
(macroexpand '(test2 y))
; ==>
; (fn* ([y__1__auto__] 
;   (if (clojure.core/pos? y)
;       (clojure.core/println y__1__auto__) 
;       (clojure.core/println (clojure.core/- y__1__auto__)))))
(defn test-it []
  (let [y -9]
    (test2 y)))
((test-it) 5) ; prints "-5"
随意尝试与您的版本。 (提示:你会得到一个异常,因为clojure.lang.Symbol不能转换为java.lang.Number)
UPDATE
既然你想基于一个常量来创建函数,你需要写一点不同的东西:
(defmacro test3 [x]
  (assert (number? x) "needs to be a compile time number")
  (if (pos? x)
      `(fn [y#] (println y#))
      `(fn [y#] (println (- y#)))))
  现在如果你使用(test3 x)你会得到一个错误,因为x不是一个数字,而是在评估时得到你想要的(test3 -10)因为-10是一个我们可以使用编译时间的数字。  我不确定你会注意到速度的提高,因为这些算法不算繁重。 
