您好我正在寻找一种方法来计算clojure中的函数调用,以便例如我可以找出最常调用的函数。理想情况下,我希望这对用户是透明的,这样如果他们添加了一个他们不知道或不关心这个过程的功能。任何帮助将不胜感激。
提前感谢你 迈克尔
答案 0 :(得分:6)
您可以将通话计数存储在atom
中,并使用with-meta
附加该功能的访问者:
(def sqrt
(let [n (atom 0)]
(with-meta
(fn [x]
(swap! n inc)
(Math/sqrt x))
{::call-count (fn [] @n)})))
示例:
((::call-count (meta sqrt))) ;=> 0
(sqrt 0) ;=> 0.0
(sqrt 1) ;=> 1.0
(sqrt 2) ;=> 1.4142135623730951
((::call-count (meta sqrt))) ;=> 3
(sqrt 3) ;=> 1.7320508075688772
(sqrt 4) ;=> 2.0
(sqrt 5) ;=> 2.23606797749979
((::call-count (meta sqrt))) ;=> 6
在某些情况下,这可能会导致相当大的减速,但计数将始终正确更新,因为Clojure原子是线程安全的。另一种方法可能是使用add-watch
而不是deref
,但哪一种更好取决于您的情况。如果你愿意,你甚至可以使用两者。
您可以使用defcounted
宏来抽象细节以定义调用计数函数,并使用call-count
函数来检索调用计数函数的调用计数:
(defmacro defcounted [sym params & body]
`(def ~sym
(let [n# (atom 0)]
(with-meta
(fn ~params
(swap! n# inc)
~@body)
{::call-count (fn [] @n#)}))))
(defn call-count [f]
((::call-count (meta f))))
(defcounted sqrt [x]
(Math/sqrt x))
示例:
(call-count sqrt) ;=> 0
(sqrt 0) ;=> 0.0
(sqrt 1) ;=> 1.0
(sqrt 2) ;=> 1.4142135623730951
(call-count sqrt) ;=> 3
(sqrt 3) ;=> 1.7320508075688772
(sqrt 4) ;=> 2.0
(sqrt 5) ;=> 2.23606797749979
(call-count sqrt) ;=> 6
此外,由于您在此处将元数据附加到函数本身而不是var,您可以将此技术扩展到匿名函数。
显然defcounted
缺少很多defn
的功能,因此对用户来说并不是真的透明。在修复此问题时,您可以使用clojure.spec
更轻松地解析defn
式参数,但我会留下您认为合适的内容,因为它可以与这个问题正交。