34 votes

Clojure idiomatique pour les rapports d'étape?

Comment dois-je suivre l'avancement d'un mappé fonction en clojure?

Lors du traitement des dossiers dans un langage impératif, j'ai souvent l'impression d'un message de temps en temps pour indiquer dans quelle mesure les choses ont disparu, par exemple, de reporting toutes les 1000 enregistrements. Essentiellement, cela est en train de compter les répétitions de boucles.

Je me demandais quelles sont les approches que j'ai pu prendre à cette clojure où je suis cartographie d'une fonction sur mon séquence d'enregistrements. Dans ce cas, l'impression, le message (et même compte tenu de la progression) semblent être essentiellement des effets secondaires.

Ce que je suis venu jusqu'ici ressemble à ceci:

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

Existe-il d'autres (mieux) les moyens de la réalisation de cet effet?

Existe-il des pièges dans ce que je fais? (Je pense que je suis la préservation de la paresse et de ne pas tenir la tête par exemple.)

33voto

Arthur Ulfeldt Points 45059

La grande chose à propos de clojure est que vous pouvez attacher le rapport aux données elles-mêmes au lieu du code qui fait l'informatique. Cela vous permet de séparer ces parties logiquement distinctes. Voici un morceau de mon misc.clj que je trouve que j'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 enroulez le reporter autour de vos données, puis transmettez le résultat à la fonction de traitement.

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

6voto

Dan Points 4107

J'effectuerais probablement le reportage dans un agent. Quelque chose comme ça:

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

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

4voto

Nicolas Buduroi Points 2558

Je ne sais pas de toutes façons de le faire, il serait peut-être une bonne idée de parcourir clojure.contrib de la documentation pour voir si il y a déjà quelque chose. En attendant, j'ai regardé votre exemple et nettoyé 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 plus généralisée de la version de votre rapport de progrès de la fonction. Cette fonction serait de prendre une carte-comme la fonction, la fonction qui doit être mappé, un rapport de fonction et un ensemble de collections (ou une valeur de départ et une collection pour les essais de réduction).

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

Le seq? une partie est là uniquement pour une utilisation avec réduire ce qui ne veut pas revient nécessairement une séquence. Avec cette fonction, nous pouvons réécrire votre 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 filtre:

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 fonction de réduction:

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X