7

Estoy usando Clojure y necesito ejecutar una pequeña simulación. Tengo un vector de longitud n (n suele ser entre 10 y 100) que contiene valores. En cada ronda de simulación (quizás 1000 rondas juntas), uno de los valores en el vector se actualiza aleatoriamente. Supongo que podría hacer esto usando una matriz Java y llamando al método aset, pero esto rompería la expresión de programación funcional/inmutabilidad.¿Cómo hacer una simulación numérica con datos inmutables en Clojure?

¿Hay una forma más funcional de hacerlo, o debería ir con la matriz de Java?

Respuesta

6
(defn run-sim [arr num-iters update-fn] 
(if (zero? num-iters) 
    arr 
    (let [i (rand-int (count arr)) 
     x (update-fn)] 
    (println "setting arr[" i "] to" x) 
    (recur (assoc arr i x) (dec num-iters) update-fn)))) 

user> (run-sim [1 2 3 4 5 6 7 8 9 10] 10 #(rand-int 1000)) 
setting arr[ 8 ] to 167 
setting arr[ 4 ] to 977 
setting arr[ 5 ] to 810 
setting arr[ 5 ] to 165 
setting arr[ 3 ] to 486 
setting arr[ 1 ] to 382 
setting arr[ 4 ] to 792 
setting arr[ 8 ] to 478 
setting arr[ 4 ] to 144 
setting arr[ 7 ] to 416 
[1 382 3 486 144 165 7 416 478 10] 

No obstante, no es una pena usar una matriz de Java si la necesita. Especialmente si lo necesitas para ir rápido. Limite la mutación de la matriz al interior de su función (clone la matriz de entrada y trabaje en eso tal vez) y nadie será más sabio.

+0

¡Gracias! Esto parece una buena manera de resolver el problema. –

1

No es que Clojure no le permita cambiar los valores, es un poco más engorroso.

(def vec-ref (ref my-vector)) 

(dosync (set! vec-ref (assoc my-vector index value)) 

para ver los valores en el vector modificado, utilice @ vec-ref.

Podría estar fuera de detalles - Desafortunadamente, no estoy cerca de un REPL. Pero debería ayudarte a comenzar.

+0

Gracias, también voy a comprobar esta forma de resolver el problema. –

2

Primero definamos una función que actualiza un índice aleatorio en un vector con un nuevo valor. Tenga en cuenta que el vector original no se cambia, en lugar de un nuevo vector (con el valor actualizado) se devuelve:

(defn f [xs] 
    (let [r (java.util.Random.) 
     i (.nextInt r (count xs)) 
     b (.nextBoolean r)] 
    (assoc xs i ((if b inc dec) (xs i))))) 

Esta función elige un índice y luego se aumenta o disminuye el valor de ese índice en 1. Usted debe, por supuesto, cambiar esta función a sus necesidades.

entonces es una simple cuestión de componer esta función consigo mismo tantas veces que desea ejecutar la simulación:

user=> ((apply comp (repeat 1000 f)) [0 0 0 0 0 0 0]) 
[7 -4 7 6 10 0 -6] 
+0

Gracias por la respuesta, este enfoque de composición también parece interesante. –

5

Agregando a la respuesta de Brian: Si necesita más velocidad, también se puede recurrir a los transitorios .

(defn run-sim 
    [vektor num-iters update-fn] 
    (loop [vektor (transient vektor) 
     num-iters (int num-iters)] 
    (if (zero? num-iters) 
     (persistent! vektor) 
     (let [i (rand-int (count vektor)) 
      x (update-fn)] 
     (recur (assoc! vektor i x) (dec num-iters)))))) 
+0

¡Gracias! Tendré que profundizar en esas cosas transitorias, ¡la velocidad siempre es buena! –

+1

Los transitorios son fáciles: llame (cosa transitoria) al principio. Agrega un ! a las operaciones, ej. assoc !, dissoc! etc. llamada (persistente! transient-thing) al regresar. Sin embargo, no debe perder transitorios fuera de su función. – kotarak

Cuestiones relacionadas