2016-12-13 29 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樣式的參數,但是我會留下這個讓你按照你認爲合適的方式去做,因爲它與這個問題是正交的。