programming languages functional example course books functional-programming lisp clojure imperative-programming

functional programming - languages - Clojure idiomático para informes de progreso?



functional programming python (4)

He tenido este problema con algunas aplicaciones de ejecución lenta (por ejemplo, ETL de base de datos, etc.). Lo resolví agregando la función (tupelo.misc/dot ...) a la biblioteca de tupelo . Muestra:

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

Salida:

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

Los documentos API para el espacio de nombres tupelo.misc se pueden encontrar aquí .

¿Cómo debo controlar el progreso de una función mapeada en clojure?

Al procesar registros en un lenguaje imperativo, a menudo imprimo un mensaje de vez en cuando para indicar qué tan lejos han ido las cosas, por ejemplo, informar cada 1000 registros. Esencialmente esto es contar repeticiones de bucle.

Me preguntaba qué enfoques podría llevar a esto en Clojure, donde estoy mapeando una función sobre mi secuencia de registros. En este caso, imprimir el mensaje (e incluso contar el progreso) parece ser esencialmente un efecto secundario.

Lo que se me ocurrió hasta ahora se ve así:

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

Por ejemplo:

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)

¿Hay otras (mejores) formas de lograr este efecto?

¿Hay algún escollo en lo que estoy haciendo? (Creo que estoy conservando la pereza y no agarrando la cabeza, por ejemplo).


Lo mejor de clojure es que puedes adjuntar el informe a los datos en sí en lugar del código que hace la informática. Esto le permite separar estas partes lógicamente distintas. Aquí hay un fragmento de mi misc.clj que encuentro que uso en casi todos los proyectos:

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

luego envuelva al periodista alrededor de sus datos y luego pase el resultado a la función de procesamiento.

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


No conozco ninguna forma de hacerlo, tal vez sería una buena idea buscar en la documentación de clojure.contrib para ver si ya hay algo. Mientras tanto, miré tu ejemplo y lo aclare un poco.

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

Estás yendo en la dirección correcta, aunque este ejemplo es demasiado simple. Esto me dio una idea sobre una versión más generalizada de su función de informe de progreso. Esta función tomaría una función tipo mapa, la función a mapear, una función de informe y un conjunto de colecciones (o un valor de inicialización y una colección para probar reducir).

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

¿El seq? la parte está allí solo para su uso con reduce, que no necesariamente devuelve una secuencia. Con esta función, podemos reescribir su ejemplo de esta manera:

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)

Pruebe la función de 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 incluso la función de reducción:

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


Probablemente realice los informes en un agente. Algo como esto:

(defn report [a] (println "Done " s) (+ 1 s)) (let [reports (agent 0)] (map #(do (send reports report) (process-data %)) data-to-process)