如何在clojure宏中测试未评估的函数?

时间:2016-12-06 17:05:53

标签: clojure macros eval

我想评估一个宏的参数是否是一个函数。如果是,我想返回未评估的功能。否则返回不同的功能。像这样:

(defmacro func? [function]
  (if (fn? function)
     function
     identity))
((func? "oops") 5) ;=> 5 
((func? -) 5)      ;=> 5

但是上面的失败是因为未评估的函数还不是真正的函数才能使它工作我必须使用邪恶的eval:

(defmacro func? [function]
  (if (fn? (eval function))
     function
     identity))
((func? "oops") 5) ;=> 5 
((func? -) 5)      ;=> -5

如何避免在这里使用eval? 答案必须是宏。

在我的情况下宏的功能的原因是这个宏被用在我的游戏中多次调用的许多函数中。宏是输出格式解释器。换句话说,我可以自己编写这样的方式,但宏使我的代码更清晰。如果我使用的是一个函数而不是这个宏,它就会做同样的解释,只需要做一次,每50ms我的游戏迭代约60次。希望这能澄清而不是蒙混过关。

2 个答案:

答案 0 :(得分:2)

为什么要使用宏?

(defn func? [function]
  (if (fn? function)
     function
     identity))

((func? "oops") 5) ;=> 5 
((func? -) 5)      ;=> -5

请注意,#(identity %)只是identity

答案 1 :(得分:2)

你需要考虑宏的许多可能的参数,以决定它是否是一个函数。

  1. 这是一个象征吗?一个号码?一串?
  2. 符号解析为函数变量?
  3. 需要解析符号的名称空间?
  4. 假设要在当前命名空间中解析live的函数,你可以这样做:

    (defn test
      []
      "result")
    
    (defmacro func?
      [f]
      (if (and (symbol? f)
               (fn? (var-get (ns-resolve *ns* f))))
        f
        identity))
    
    (func? test)
    #object[user$test 0x37c27452 "prisma.server$test@37c27452"]
    
    (func? 4)
    #object[clojure.core$identity 0x3a811e64 "clojure.core$identity@3a811e64"]
    

    ns-resolvevar-get为您提供了正确函数值的句柄(或者对于所有情况的任何值,f确实是一个符号解析的东西)。

    从当前命名空间工作解析的所有函数,例如:

    (:require [some.ns :as other-ns])
    
    (func? other-ns/valid-fn)
    => #object[some.ns$valid-fn 0x7207a00b "some.ns$valid-fn@7207a00b"]