cj3kim
cj3kim

Reputation: 196

Clojure - What can I do to correctly use Vars and/or Atoms?

I'm having a lot of trouble with Clojure since I'm new to it. I'm attempting to solve for an orthanormal vector from matrix [[i, j, k], [5, 2, 3], [8, 3, 2]]. I know what the algorithm is, but I'm confused about when to use atoms and vars. As a result, I continue to get errors. Could someone point out what I'm doing wrong and how I can improve the code?

Listed below the code is the error.

(defn solve [& rows]   
  (let [orthanormal-vector (atom [])] 

    (let [dimension-range (range (count rows))
          determinant-argument (atom [])] 
      (doseq [[i row] (map list dimension-range rows)]
        (let [row-range (range (count row))
              determinant-vector (atom [])]        
                (doseq [[j e] (map list row-range (seq row))]
                  (if-not (= i j) (swap! determinant-vector (conj determinant-vector e)) (println "Do nothing")))        
              (var-set determinant-argument (conj determinant-argument determinant-vector)))

              (def determinant-result (incanter-core/det (incanter-core/matrix (vec determinant-argument))))
        (swap! orthanormal-vector (conj orthanormal-vector determinant-result))
 ))))

java.lang.ClassCastException: clojure.lang.Atom cannot be cast to clojure.lang.IPersistentCollection
              core.clj:83 clojure.core/conj
/Users/chriskim/Desktop/promethix/src/promethix/orthanormal/solver.clj:14 promethix.orthanormal.solver/solve
          RestFn.java:436 clojure.lang.RestFn.invoke
/Users/chriskim/Desktop/promethix/src/promethix/orthanormal/solver.clj:24 promethix.orthanormal.solver/eval16321
       Compiler.java:6703 clojure.lang.Compiler.eval
       Compiler.java:6666 clojure.lang.Compiler.eval
            core.clj:2927 clojure.core/eval
              eval.clj:77 lighttable.nrepl.eval/->result
             AFn.java:156 clojure.lang.AFn.applyToHelper
             AFn.java:144 clojure.lang.AFn.applyTo
             core.clj:626 clojure.core/apply
            core.clj:2468 clojure.core/partial[fn]
          RestFn.java:408 clojure.lang.RestFn.invoke
            core.clj:2559 clojure.core/map[fn]
          LazySeq.java:40 clojure.lang.LazySeq.sval
          LazySeq.java:49 clojure.lang.LazySeq.seq
              RT.java:484 clojure.lang.RT.seq
             core.clj:133 clojure.core/seq
            core.clj:2595 clojure.core/filter[fn]
          LazySeq.java:40 clojure.lang.LazySeq.sval
          LazySeq.java:49 clojure.lang.LazySeq.seq
             Cons.java:39 clojure.lang.Cons.next
              RT.java:598 clojure.lang.RT.next
              core.clj:64 clojure.core/next
            core.clj:2856 clojure.core/dorun
            core.clj:2871 clojure.core/doall
             eval.clj:126 lighttable.nrepl.eval/eval-clj
          RestFn.java:442 clojure.lang.RestFn.invoke
             eval.clj:192 lighttable.nrepl.eval/eval10834[fn]
             AFn.java:152 clojure.lang.AFn.applyToHelper
             AFn.java:144 clojure.lang.AFn.applyTo
             core.clj:624 clojure.core/apply
            core.clj:1862 clojure.core/with-bindings*
          RestFn.java:425 clojure.lang.RestFn.invoke
             eval.clj:177 lighttable.nrepl.eval/eval10834[fn]
             eval.clj:176 lighttable.nrepl.eval/eval10834[fn]
         MultiFn.java:227 clojure.lang.MultiFn.invoke
              core.clj:98 lighttable.nrepl.core/queued[fn]
            core.clj:2402 clojure.core/comp[fn]
interruptible_eval.clj:138 clojure.tools.nrepl.middleware.interruptible-eval/run-next[fn]
              AFn.java:22 clojure.lang.AFn.run
ThreadPoolExecutor.java:895 java.util.concurrent.ThreadPoolExecutor$Worker.runTask
ThreadPoolExecutor.java:918 java.util.concurrent.ThreadPoolExecutor$Worker.run
          Thread.java:680 java.lang.Thread.run

Upvotes: 0

Views: 713

Answers (2)

mikera
mikera

Reputation: 106401

This is not quite answering your question but I'd suggest using core.matrix for matrix computation / algorithms in Clojure. This gives you at least two big advantages:

  • A lot of vector / matrix operations are already implemented. There isn't much point reinventing the wheel if you don't need to.
  • It allows you to use optimised implementations that are much more efficient than regular Clojure data structures. This includes native BLAS libraries (e.g. Clatrix) and fast pure-JVM matrix libraries (e.g. Vectorz)

Disclaimer: I'm a maintainer on some of the above projects.

Upvotes: 1

Arthur Ulfeldt
Arthur Ulfeldt

Reputation: 91607

The specific error resulting in clojure.lang.Atom cannot be cast to clojure.lang.IPersistentCollection is:

(swap! determinant-vector (conj determinant-vector e))

shoudl be written:

(swap! determinant-vector conj e)

swap! takes an atom as it's first argument and a function as a second argument. It then builds a function call by passing the current value in the atom as the first argument to that function and appends the rest of the arguments after that. this will be translated into a function call somewhat like this:

(conj @determinant-vector e)

ps: @ is a reader macro for calling deref to get the current value out of one of the mutable state types.

PPS: Charles Duffy is correct that loop/recur are much better tools for this task than using an atom. It should be about 100x faster, at least in terms of overhead.

Upvotes: 1

Related Questions