我应该如何监控clojure中映射函数的进度?
当用命令式语言处理记录时,我经常会经常打印一条消息,以指示事情已经走了多远,例如:报告每1000条记录。基本上这是计算循环重复。
我想知道在clojure中我可以采取什么方法,我将一个函数映射到我的记录序列。在这种情况下,打印消息(甚至保持进度计数)似乎基本上是副作用。
到目前为止,我想出的是:
(defn report
[report-every val cnt]
(if (= 0 (mod cnt report-every))
(println "Done" cnt))
val)
(defn report-progress
[report-every aseq]
(map (fn [val cnt]
(report report-every val cnt))
aseq
(iterate inc 1)))
例如:
user> (doall (report-progress 2 (range 10)))
Done 2
Done 4
Done 6
Done 8
Done 10
(0 1 2 3 4 5 6 7 8 9)
是否有其他(更好)方法可以达到这种效果?
我在做什么有任何陷阱? (我认为我保留了懒惰而不是举个例子。)
答案 0 :(得分:32)
关于clojure的好处是你可以将报告附加到数据本身而不是计算代码。这允许您分离这些逻辑上不同的部分。这是我的misc.clj中的一个块,我发现我几乎在每个项目中使用它:
(defn seq-counter
"calls callback after every n'th entry in sequence is evaluated.
Optionally takes another callback to call once the seq is fully evaluated."
([sequence n callback]
(map #(do (if (= (rem %1 n) 0) (callback)) %2) (iterate inc 1) sequence))
([sequence n callback finished-callback]
(drop-last (lazy-cat (seq-counter sequence n callback)
(lazy-seq (cons (finished-callback) ()))))))
然后将报告者包裹在您的数据周围,然后将结果传递给处理函数。
(map process-data (seq-counter inc-progress input))
答案 1 :(得分:6)
我可能会在代理商中执行报告。像这样:
(defn report [a]
(println "Done " s)
(+ 1 s))
(let [reports (agent 0)]
(map #(do (send reports report)
(process-data %))
data-to-process)
答案 2 :(得分:4)
我不知道有任何现有的方法,也许浏览clojure.contrib文档以查看是否已经存在某些内容可能是个好主意。与此同时,我看了你的例子,并将其清理了一下。
(defn report [cnt]
(when (even? cnt)
(println "Done" cnt)))
(defn report-progress []
(let [aseq (range 10)]
(doall (map report (take (count aseq) (iterate inc 1))))
aseq))
你正朝着正确的方向前进,即使这个例子太简单了。这让我对您的报告进度函数的更通用版本有所了解。此函数将采用类似map的函数,要映射的函数,报告函数和一组集合(或种子值和用于测试的集合reduce)。
(defn report-progress [m f r & colls]
(let [result (apply m
(fn [& args]
(let [v (apply f args)]
(apply r v args) v))
colls)]
(if (seq? result)
(doall result)
result)))
seq?部分只用于减少而不是 必然会返回一个序列。有了这个功能,我们可以重写你的 像这样的例子:
user>
(report-progress
map
(fn [_ v] v)
(fn [result cnt _]
(when (even? cnt)
(println "Done" cnt)))
(iterate inc 1)
(range 10))
Done 2
Done 4
Done 6
Done 8
Done 10
(0 1 2 3 4 5 6 7 8 9)
测试过滤功能:
user>
(report-progress
filter
odd?
(fn [result cnt]
(when (even? cnt)
(println "Done" cnt)))
(range 10))
Done 0
Done 2
Done 4
Done 6
Done 8
(1 3 5 7 9)
甚至还原功能:
user>
(report-progress
reduce
+
(fn [result s v]
(when (even? s)
(println "Done" s)))
2
(repeat 10 1))
Done 2
Done 4
Done 6
Done 8
Done 10
12
答案 3 :(得分:0)
我遇到了一些运行缓慢的应用程序(例如数据库ETL等)的问题。我通过添加函数(tupelo.misc/dot ...)
to the tupelo library解决了这个问题。样品:
(ns xxx.core
(:require [tupelo.misc :as tm]))
(tm/dots-config! {:decimation 10} )
(tm/with-dots
(doseq [ii (range 2345)]
(tm/dot)
(Thread/sleep 5)))
输出:
0 ....................................................................................................
1000 ....................................................................................................
2000 ...................................
2345 total
tupelo.misc命名空间can be found here的API文档。