Question

Comment dois-je suivre les progrès d'une fonction mappée dans Clojure?

Lors du traitement des documents dans un langage impératif imprimer souvent tout aussi souvent un message pour indiquer comment les choses ont bien disparu, par exemple rapports 1000 dossiers. Essentiellement, cela compte répétitions de la boucle.

Je me demandais ce que je pouvais prendre des approches à cela dans clojure où je délimiterai une fonction sur ma séquence d'enregistrements. Dans ce cas, l'impression du message (et le nombre même garder des progrès) semblent être essentiellement des effets secondaires.

Ce que je suis venu avec si loin ressemble à:

(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)))

Par exemple:

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)

Y at-il d'autres (mieux) les moyens de parvenir à cet effet?

Y a-t-il des pièges dans ce que je fais? (Je pense que je suis la paresse et non la préservation tenant la tête par exemple).

Était-ce utile?

La solution

La grande chose au sujet Clojure est que vous pouvez joindre le reporting aux données lui-même au lieu du code qui fait le calcul. Cela vous permet de séparer ces parties logiquement distinctes. Voici un morceau de mon misc.clj que je trouve que je l'utilise dans à peu près tous les projets:

(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) ())))))) 

puis enveloppez le journaliste autour de vos données et les transmettre ensuite le résultat à la fonction de traitement.

(map process-data (seq-counter inc-progress input))

Autres conseils

Je serais probablement effectuer la déclaration dans un agent. Quelque chose comme ceci:

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

(let [reports (agent 0)]
  (map #(do (send reports report)
            (process-data %))
       data-to-process)

Je ne connais pas de moyens existants de le faire, peut-être que ce serait une bonne idée de parcourir la documentation de clojure.contrib regarder s'il y a déjà quelque chose. En attendant, je l'ai regardé votre exemple et parvient à claquer un peu.

(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))

Vous vous dirigez dans la bonne direction, même si cet exemple est trop simple. Cela m'a donné une idée sur une version plus généralisée de votre fonction progression du rapport. Cette fonction prendrait une fonction similaire à une carte, la fonction à cartographier, une fonction de rapport et un ensemble de collections (ou une valeur de départ et une collection pour les tests réduire).

(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)))

Les suivants? une partie est là que pour une utilisation avec réduction qui ne nécessairement renvoie une séquence. Avec cette fonction, on peut réécrire votre par exemple comme ceci:

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)

Test de la fonction de filtrage:

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)

Et même la diminution de la fonction:

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

Je l'ai eu ce problème avec des applications en cours d'exécution-lente (par exemple la base de données ETL, etc.). Je l'ai résolu en ajoutant la fonction (tupelo.misc/dot ...) à la bibliothèque de Tupelo . Echantillon:

(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)))

Sortie:

     0 ....................................................................................................
  1000 ....................................................................................................
  2000 ...................................
  2345 total

API docs pour l'espace de noms tupelo.misc peuvent être trouvés ici .

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top