34

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)

この効果を達成する他の(より良い)方法はありますか?

私がしていることに落とし穴はありますか?(例えば、私は怠惰を保ち、頭を抱えていないと思います。)

4

4 に答える 4

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))
于 2010-01-07T20:17:53.817 に答える
6

私はおそらくエージェントでレポートを実行します。このようなもの:

(defn report [a]
  (println "Done " s)
  (+ 1 s))

(let [reports (agent 0)]
  (map #(do (send reports report)
            (process-data %))
       data-to-process)
于 2010-01-08T11:06:47.197 に答える
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))

この例は単純すぎますが、あなたは正しい方向に向かっています。これにより、レポート進行関数のより一般化されたバージョンについてのアイデアが得られました。この関数は、マップのような関数、マップされる関数、レポート関数、およびコレクションのセット(またはシード値とテスト用のコレクション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)))

シーケンス?一部はreduceでのみ使用するためのものであり、必ずしもシーケンスを返すとは限りません。この関数を使用すると、例を次のように書き直すことができます。

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)

そしてreduce関数でさえ:

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
于 2010-01-07T20:59:12.450 に答える
-1

実行速度の遅いアプリ(データベースETLなど)でこの問題が発生しました。(tupelo.misc/dot ...) tupeloライブラリに関数を追加することで解決しました。サンプル:

(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名前空間のAPIドキュメントはここにあります

于 2016-10-05T02:32:50.383 に答える