2016-12-13 30 views
1

您好我正在寻找一种方法来计算clojure中的函数调用,以便例如我可以找出哪些函数被调用最频繁。理想情况下,我希望这对用户是透明的,这样如果他们添加一个他们不知道或关心这个过程的函数。任何帮助将不胜感激。在clojure计数函数调用

请多关照 迈克尔

回答

5

可以使用with-meta存储呼叫计数的atom并附加一个访问该功能:

(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原子s是线程安全的。另一种方法是使用add-watch而不是deref,但哪一个更好取决于你的情况。如果你愿意,你甚至可以同时使用它们。

你可以抽象掉了defcounted宏来定义呼叫计数功能的细节和call-count功能取回调用计数函数的调用次数:

(defmacro defcounted [sym params & body] 
    `(def ~sym 
    (let [n# (atom 0)] 
     (with-meta 
     (fn ~params 
      (swap! n# inc) 
      [email protected]) 
     {::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样式的参数,但是我会留下这个让你按照你认为合适的方式去做,因为它与这个问题是正交的。