進捗報告のための慣用Clojureの?
-
19-09-2019 - |
質問
私は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)
は、この効果を達成するのが他の(良い)方法はありますか?
すべての落とし穴は、私がやっているものの中にありますか? (私は怠惰を維持し、例えば頭を保持していないと思う。)
解決
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))
他のヒント
私はおそらく、エージェントに報告を行います。このような何かます:
(defn report [a]
(println "Done " s)
(+ 1 s))
(let [reports (agent 0)]
(map #(do (send reports report)
(process-data %))
data-to-process)
私は多分何かがすでにあるかどう見て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))
この例では、あまりにもシンプルであっても、正しい方向に向かっています。これは、私にあなたのレポート進行機能のより一般化されたバージョンについての考えを与えました。この機能は、マップのような機能、マッピングする機能、レポート機能やコレクションのセット(または減らすテストするためのシード値とコレクションを)取るます。
(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)))
配列?一部しかない削減で使用するためにあり 必ずしもシーケンスを返します。この機能により、私たちはあなたを書き換えることができます このような例:
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
私はいくつかの実行速度の遅いアプリケーション(例えばデータベースETLなど)でこの問題がありました。私はテュペロライブラリのに機能(tupelo.misc/dot ...)
を追加することによって、それを解決しました。サンプルます:
(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
のためのAPIドキュメントtupelo.misc名前空間ここを見つけることができますます。