计算clojure中的函数调用

Mic*_*eon 1 functional-programming clojure

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

迈克尔,先谢谢你

Sam*_*tep 6

您可以将呼叫计数存储在a中,atom并使用以下命令附加该功能的访问者with-meta:

(def sqrt
  (let [n (atom 0)]
    (with-meta
      (fn [x]
        (swap! n inc)
        (Math/sqrt x))
      {::call-count (fn [] @n)})))
Run Code Online (Sandbox Code Playgroud)

例子:

((::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
Run Code Online (Sandbox Code Playgroud)

在某些情况下,这可能会导致相当大的减速,但计数将始终正确更新,因为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))
Run Code Online (Sandbox Code Playgroud)

例子:

(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
Run Code Online (Sandbox Code Playgroud)

此外,由于您将元数据附加到函数本身而不是var,您可以将此技术扩展到匿名函数.

显然defcounted缺乏很多defn功能,所以它对用户来说并不是真的透明.在解决这个问题时,您可以使用clojure.spec更轻松地解析defn-style参数,但是我会留下让您按照自己的意愿进行操作,因为它与此问题正交.