2011-09-10 29 views
4

我试图写一个clojure宏,让我调用一个函数并从地图/结构中检索参数所提供的密钥值,如:Clojure宏抛出“CompilerException java.lang.IllegalStateException:Var clojure.core/unquote未绑定”当调用

(with-params {:apple 2 :banana 3 :cherry 7} + :apple :banana) 
    ;; 5 

,但是当我尝试使用我写的宏:

(defmacro with-params [s f & symbols] 
    `(~f [email protected](map ~s ~symbols))) 

调用

(with-params {:apple 2 :banana 3 :cherry 7} + :apple :banana) 

给我

#<CompilerException java.lang.IllegalStateException: Var clojure.core/unquote is unbound. (NO_SOURCE_FILE:0)> 

有人能帮助我了解如何语法,引用的作品吗?

回答

3

之所以`(~f [email protected](map ~s ~symbols))不起作用是编译器扼流圈上的不必要unquote~)的unquote-splicing[email protected])的内部。 unquote-splicing引用外部syntax-quote`),所以内部两个unquote s没有任何匹配syntax-quote,这就是为什么你得到“未绑定”的错误。

你想要做的就是首先评估(map s symbols)得到操作数的序列,然后将展平的结果传递给函数(~f);因此,正确的版本是:

(macroexpand '(with-params {:a 1 :b 2 :c 5} * :a :b :c)) ;; (* 1 2 5) 
(with-params {:a 1 :b 2 :c 5} * :a :b :c)     ;; 10 
+0

感谢您的建议!但是您能澄清一下为什么在map之前放置'〜@'会导致编译器出现问题吗?我认为我可以通过拼接地图的结果来制作一个列表表达式,但是我认为在这种情况下“应用”确实效果更好。 –

+0

@Scott:经过一番思考之后,我想出了本来应该有的解决方案。希望现在更清楚一点。如果我的回答很有帮助,您可以对其进行修改或将其标记为已接受。 –

+2

严格地说它应该是(defmacro with-params [sf&symbols]'(〜f〜@(map(fn [symbol](list symbol s))symbols)),因为这将允许map /运行时间 –

4

对于它的价值,这不应该是一个宏观反正:

(defmacro with-params [s f & symbols] `(~f [email protected](map s symbols))) 

您可以轻松地验证这一点。一个函数功能非常强大,只有当map和关键字都是编译时文字时,宏才会有效。

(defmacro with-params-macro [s f & symbols] 
    `(~f [email protected](map s symbols))) 

(defn with-params-fn [s f & symbols] 
    (apply f (map s symbols))) 

user> (with-params-macro {:x 1} (fn [z] z) :x) 
1 
user> (let [params {:x 1}] 
     (with-params-macro params (fn [z] z) :x)) 
nil 

user> (let [params {:x 1}] 
     (with-params-fn params (fn [z] z) :x)) 
1 
+0

+1非常好的一点*原始问题*是关于宏的,我们经常看到,有时候不问这个问题是很好的。 –

相关问题