How to do numerical simulations with immutable data in Clojure?

I am using Clojure and I need to run a little simulation. I have a vector of length n (n usually ranges from 10 to 100) that contains values. On each round of modeling (maybe 1000 rounds together), one of the values ​​in the vector is updated randomly. I think I could do this by using a Java array and calling the aset method, but this will break the functional programming / immutability of the idiom.

Is there a more functional way to do this, or should I just go with a Java array?

+7
immutability arrays functional-programming clojure numerical-computing
source share
4 answers
(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 ix) (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] 

There is no shame in using a Java array if you need it. Especially if you need to go fast. Limit the mutation of the array inside your function (clone the input array and work on it, maybe) and no one will become wiser.

+6
source share

Addendum to Brian's answer: if you need a higher speed, you can also resort to transients.

 (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 ix) (dec num-iters)))))) 
+5
source share

Lets you first define a function that updates a random index in a vector with a new value. Note that the original vector does not change; instead, a new vector is returned (with the updated value):

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

This function selects the index, and then either increases or decreases the value in this index by 1. You, of course, must change this function to suit your needs.

Then just write this function with you as many times as you want to run the simulation:

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

It's not that Clojure won't let you change values, it's a little more cumbersome.

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

to see values ​​in a modified vector, use @ vec-ref.

I can not be in the details - unfortunately, I am not next to the REPL. But that should get you started.

+1
source share

All Articles