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

How to test unevaluated functions in clojure macros?

我想评估宏的参数是否为函数。如果是,我想 return 未计算的函数。否则 return 一个不同的函数。像这样:

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

但是上面的失败了,因为未评估的函数还不是一个真正的函数为了让它工作我必须使用邪恶的评估:

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

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

在我的例子中使用宏而不是函数的原因是这个宏在我的游戏中被多次调用的许多函数中使用。该宏是一个输出格式解释器。换句话说,我可以自己写成那样,但宏使我的代码更清晰。如果我使用一个函数而不是这个宏,它会做同样的解释,只需要做一次,我的游戏每 50 毫秒迭代大约 60 次。希望这能澄清而不是使我的情况蒙上阴影。

为什么要使用宏?

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

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

请注意 #(identity %) 只是 identity

您需要考虑宏的许多可能参数,以确定它是否是函数。

  1. 是符号吗?一个号码?字符串?
  2. 符号解析为作为函数的 var?
  3. 需要在哪个命名空间中解析该符号?

假设要解析的函数存在于当前命名空间中,您可以这样做:

(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"]