一个创建函数的函数,可以查询它们被调用的次数
A function that creates functions that can be interrogated for the number of times they have been called
我想创建一个函数来创建其他函数,这些函数会记住它们被调用了多少次 -- 并不难。但我想在未来的某个任意时间询问这些功能,而不是在被调用后立即询问。它可能是这样工作的:
(defn mk-cntr []
(let [count (atom 0)
res (fn cntr [] (swap! count inc))]
res))
(let [f (mk-cntr)]
(f)
(f)
; other complicated stuff here
(f)
; Interrogate it now.
(println (:count f))) ; or something similar
这确实创建了计算调用次数的函数,但我不想在调用后立即获得该信息。
我想重现这样的东西 JavaScript 片段:
function counter() {
function result() { result.count++ }
result.count = 0
return result
}
有了这个,您可以 let cntr = counter()
然后稍后像 cntr.count
一样查询它以了解它被调用了多少次。
我摸索过函数元数据,但无法想出有效的方法。感觉 defrecord
和 defprotocol
都可以用,但是在 JavaScript.
中想出这么简单的东西似乎很麻烦
有没有简单的方法来实现这个Clojure/Script?
如果您有一个固定函数,其中包含您想要跟踪的已知数量的参数,您可以创建一个记录并为所需的数量实现 IFn
:
(defrecord FC [counter]
IFn
(invoke [this]
(let [res (swap! counter inc)]
res)))
(defn mk-cntr []
(->FC (atom 0)))
如果您需要包装一些任意函数,我会创建一个包含包装函数和计数器的记录:
(defn mk-cntr2 [f]
(let [counter (atom 0)]
{:counter counter
:fn (fn [& args]
(swap! counter inc)
(apply f args))}))
然后您可以调用和查询:
(def f (mk-cntr2 println))
((:fn f) "first")
((:fn f) "second")
@((:counter f)) ;; 2
如果您同意在您的代码库中使用一些 Java,您可以编写一个扩展 AFn class and wraps an IFn 的 class。在你项目的 Java 源代码目录中(例如,你可以在我的 Leiningen 中有一行 :java-source-paths ["javasrc"]
)并且我有一个文件 CountedFn.java
包含以下内容:
import clojure.lang.AFn;
import clojure.lang.IFn;
import java.util.concurrent.atomic.AtomicInteger;
public class CountedFn extends AFn {
private IFn _inner;
private AtomicInteger _counter = new AtomicInteger(0);
public CountedFn(IFn inner) {
_inner = inner;
}
private void step() {
_counter.incrementAndGet();
}
public int getCount() {
return _counter.get();
}
public Object invoke(Object arg1) {
step();
return _inner.invoke(arg1);
}
// Implement 'invoke' for all other arities too... a bit of work.
}
然后你像这样使用它:
(import 'CountedFn)
(defn add119 [x]
(+ x 119))
(def f (CountedFn. add119))
(f 10000)
;; => 10119
(.getCount f)
;; => 1
(f 1000)
;; => 1119
(.getCount f)
;; => 2
Metadata comes-in 在这里很方便。
(defn counted [f]
(let [c (atom 0)]
(with-meta #(do (swap! c inc) (apply f %&))
{:count c})))
counted
包装一个函数,返回另一个在其元数据中维护调用计数的函数。按照您示例的样式:
(let [add (counted +)]
(println "add called" @(:count (meta add)) "times")
(println "add works as usual: 1+2 =" (add 1 2))
(println "add called" @(:count (meta add)) "times")
(println "sum of first 10 natural numbers is" (reduce add (range 11)))
#_more-complicated-stuff
(println "add called" @(:count (meta add)) "times"))
;; Prints the following
add called 0 times
add works as usual: 1+2 = 3
add called 1 times
sum of first 10 natural numbers is 55
add called 11 times
nil
user=>
add-watch
也是你可以订阅这个计数器的东西,so-to-speak。
例外情况也是需要考虑的事情。当包裹的 fn 抛出时必须发生什么?
我想创建一个函数来创建其他函数,这些函数会记住它们被调用了多少次 -- 并不难。但我想在未来的某个任意时间询问这些功能,而不是在被调用后立即询问。它可能是这样工作的:
(defn mk-cntr []
(let [count (atom 0)
res (fn cntr [] (swap! count inc))]
res))
(let [f (mk-cntr)]
(f)
(f)
; other complicated stuff here
(f)
; Interrogate it now.
(println (:count f))) ; or something similar
这确实创建了计算调用次数的函数,但我不想在调用后立即获得该信息。
我想重现这样的东西 JavaScript 片段:
function counter() {
function result() { result.count++ }
result.count = 0
return result
}
有了这个,您可以 let cntr = counter()
然后稍后像 cntr.count
一样查询它以了解它被调用了多少次。
我摸索过函数元数据,但无法想出有效的方法。感觉 defrecord
和 defprotocol
都可以用,但是在 JavaScript.
有没有简单的方法来实现这个Clojure/Script?
如果您有一个固定函数,其中包含您想要跟踪的已知数量的参数,您可以创建一个记录并为所需的数量实现 IFn
:
(defrecord FC [counter]
IFn
(invoke [this]
(let [res (swap! counter inc)]
res)))
(defn mk-cntr []
(->FC (atom 0)))
如果您需要包装一些任意函数,我会创建一个包含包装函数和计数器的记录:
(defn mk-cntr2 [f]
(let [counter (atom 0)]
{:counter counter
:fn (fn [& args]
(swap! counter inc)
(apply f args))}))
然后您可以调用和查询:
(def f (mk-cntr2 println))
((:fn f) "first")
((:fn f) "second")
@((:counter f)) ;; 2
如果您同意在您的代码库中使用一些 Java,您可以编写一个扩展 AFn class and wraps an IFn 的 class。在你项目的 Java 源代码目录中(例如,你可以在我的 Leiningen 中有一行 :java-source-paths ["javasrc"]
)并且我有一个文件 CountedFn.java
包含以下内容:
import clojure.lang.AFn;
import clojure.lang.IFn;
import java.util.concurrent.atomic.AtomicInteger;
public class CountedFn extends AFn {
private IFn _inner;
private AtomicInteger _counter = new AtomicInteger(0);
public CountedFn(IFn inner) {
_inner = inner;
}
private void step() {
_counter.incrementAndGet();
}
public int getCount() {
return _counter.get();
}
public Object invoke(Object arg1) {
step();
return _inner.invoke(arg1);
}
// Implement 'invoke' for all other arities too... a bit of work.
}
然后你像这样使用它:
(import 'CountedFn)
(defn add119 [x]
(+ x 119))
(def f (CountedFn. add119))
(f 10000)
;; => 10119
(.getCount f)
;; => 1
(f 1000)
;; => 1119
(.getCount f)
;; => 2
Metadata comes-in 在这里很方便。
(defn counted [f]
(let [c (atom 0)]
(with-meta #(do (swap! c inc) (apply f %&))
{:count c})))
counted
包装一个函数,返回另一个在其元数据中维护调用计数的函数。按照您示例的样式:
(let [add (counted +)]
(println "add called" @(:count (meta add)) "times")
(println "add works as usual: 1+2 =" (add 1 2))
(println "add called" @(:count (meta add)) "times")
(println "sum of first 10 natural numbers is" (reduce add (range 11)))
#_more-complicated-stuff
(println "add called" @(:count (meta add)) "times"))
;; Prints the following
add called 0 times
add works as usual: 1+2 = 3
add called 1 times
sum of first 10 natural numbers is 55
add called 11 times
nil
user=>
add-watch
也是你可以订阅这个计数器的东西,so-to-speak。
例外情况也是需要考虑的事情。当包裹的 fn 抛出时必须发生什么?