Domanda

Come posso monitorare lo stato di avanzamento di un'unità di funzione in clojure?

Durante l'elaborazione di record in un linguaggio imperativo spesso mi stampa un messaggio ogni tanto, per indicare di quanto sono andate le cose, ad es.reporting ogni 1000 record.In sostanza questo è il conteggio di ciclo di ripetizioni.

Mi chiedevo quali sono gli approcci che ho potuto prendere questo in clojure, dove io sono il mapping di una funzione sopra la mia sequenza di record.In questo caso la stampa il messaggio (e anche di tenere conto del progresso) sembrano essere essenzialmente effetti collaterali.

Che cosa sono venuto con così lontano, assomiglia a questo:

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

Per esempio:

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)

Ci sono altre a (migliore di) modi di ottenere questo effetto?

Ci sono insidie che cosa sto facendo?(Penso di preservare la pigrizia e non attenendosi al capo, per esempio.)

È stato utile?

Soluzione

La cosa grandiosa di clojure è che si può collegare il reporting dei dati stessi, anziché il codice che fa il calcolo.Questo permette di separare logicamente distinte parti.Qui è un pezzo dal mio misc.clj che mi trovo ad utilizzare in quasi ogni progetto:

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

poi avvolgere il reporter in giro i tuoi dati e poi passare il risultato della funzione di elaborazione.

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

Altri suggerimenti

Io probabilmente eseguire la segnalazione di un agente.Qualcosa di simile a questo:

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

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

Non so di qualsiasi esistente modi per farlo, forse sarebbe una buona idea per navigare in clojure.contrib documentazione di cercare se c'è già qualcosa.Nel frattempo, ho guardato il tuo esempio e cancellato un po'.

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

Si sta andando nella giusta direzione, anche se questo esempio è troppo semplice.Questo mi ha dato un'idea su una più generalizzata versione del report-funzione di progresso.Questa funzione dovrebbe prendere come una mappa di funzione, la funzione della mappatura, una funzione di report e di un insieme di collezioni (o un valore di inizializzazione e di una raccolta test per ridurre).

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

Il seq?parte c'è solo per ridurre il che non necessariamente restituisce una sequenza.Con questa funzione, possiamo riscrivere il tuo esempio come questo:

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)

Prova la funzione di filtro:

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)

E anche il ridurre la funzione:

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

Ho avuto questo problema con qualche lentezza di esecuzione di applicazioni (ad es.database ETL, ecc).Ho risolto aggiungendo la funzione (tupelo.misc/dot ...) a tupelo biblioteca.Esempio:

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

Output:

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

API docs per la tupelo.misc spazio dei nomi può essere trovato qui.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top