7

Ich benutze Clojure und ich muss eine kleine Simulation ausführen. Ich habe einen Vektor der Länge n (n ist normalerweise zwischen 10 und 100), der Werte enthält. Bei jeder Simulationsrunde (möglicherweise 1000 Runden zusammen) wird einer der Werte im Vektor zufällig aktualisiert. Ich denke, ich könnte dies tun, indem ich ein Java-Array verwende und die aset-Methode aufruft, aber dies würde das funktionale Programmier-/Unveränderlichkeits-Idiom durchbrechen.Wie macht man numerische Simulation mit unveränderlichen Daten in Clojure?

Gibt es einen funktionelleren Weg, oder sollte ich einfach mit dem Java-Array gehen?

Antwort

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] 

Es ist keine Schande, ein Java-Array zu verwenden, wenn Sie es brauchen. Vor allem, wenn Sie schnell gehen müssen. Begrenzen Sie die Array-Mutation auf das Innere Ihrer Funktion (klonen Sie das Eingabe-Array und arbeiten Sie daran), und niemand wird weiser werden.

+0

Vielen Dank! Das sieht nach einer guten Möglichkeit aus, das Problem zu lösen. –

1

Es ist nicht so, dass Clojure Sie nicht Werte ändern lassen wird, es ist nur ein bisschen mühsamer.

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

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

Um die Werte im geänderten Vektor zu betrachten, verwenden Sie @ vec-ref.

Könnte in Details sein - ich bin leider nicht in der Nähe einer REPL. Aber es sollte dich beginnen.

+0

Danke, ich werde auch diesen Weg zur Lösung des Problems überprüfen. –

2

Lässt zuerst eine Funktion definieren, die einen zufälligen Index in einem Vektor mit einem neuen Wert aktualisiert. Beachten Sie, dass der ursprüngliche Vektor nicht geändert wird, anstatt einen neuen Vektor (mit dem aktualisierten Wert) zurückgegeben:

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

Diese Funktion wählt einen Index und dann ist es entweder erhöht oder verringert den Wert an diesem Index von 1. Sie Natürlich muss diese Funktion auf Ihre Bedürfnisse angepasst werden.

Dann ist es eine einfache Sache, diese Funktion mit sich selbst so oft komponieren wollen Sie die Simulation auszuführen:

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

Danke für die Antwort, dieser Compose-Ansatz sieht auch interessant aus. –

5

zu Brians Antwort hinzu: Wenn Sie mehr Geschwindigkeit benötigen, können Sie auch auf Transienten greifen .

+0

Vielen Dank! Ich muss mich in das vergängliche Zeug stürzen, Geschwindigkeit ist immer gut! –

+1

Transienten sind einfach: Anruf (vorübergehende Sache) am Anfang. Füge hinzu ein ! zu Operationen, z. assoziieren !, dissoziieren! etc. call (persistent! transient-thing) bei der Rückkehr. Sie sollten Transienten jedoch nicht außerhalb Ihrer Funktion verlieren. – kotarak

Verwandte Themen