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是一个我们可以使用编译时间的数字。 我不确定你会注意到速度的提高,因为这些算法不算繁重。

链接地址: http://www.djcxy.com/p/65769.html

上一篇: Clojure macros: quoting and syntax quoting

下一篇: unquote a list in Clojure macro