Clojure — Что я могу сделать, чтобы правильно использовать переменные и / или атомы?

#variables #clojure

#переменные #clojure

Вопрос:

У меня много проблем с Clojure, так как я новичок в этом. Я пытаюсь решить для ортанормального вектора из матрицы [[i, j, k], [5, 2, 3], [8, 3, 2]]. Я знаю, что это за алгоритм, но я не понимаю, когда использовать атомы и переменные. В результате я продолжаю получать ошибки. Может кто-нибудь указать, что я делаю неправильно и как я могу улучшить код?

Ниже кода указана ошибка.

 (defn solve [amp; 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
 

Комментарии:

1. В общем , doseq , атомы и переменные — это неправильные инструменты, если вы выполняете вычисления, а не генерируете побочные эффекты. Рассмотрим цикл / повторение.

2. … тем не менее, иногда уместно иметь атом и вводить новые значения во время вычисления (хотя здесь вы хотели reset! бы, а не swap! ). В принципе, никогда не стоит вызывать def внутри функции.

3. … кроме того, если вы собираетесь использовать атомы, вам нужно, чтобы deref они выводили значения. (Но, опять же, не используйте атомы здесь; локальные привязки гораздо более уместны).

4. Именно то, что сказал @CharlesDuffy. Вам не нужны (и вы не должны использовать) ни атомы, ни ссылки для этого

5. Вы предлагаете мне вместо этого использовать вложенные блоки let-форм? Я в замешательстве, потому что «Локальные, созданные с помощью let, не являются переменными. После создания их значения никогда не меняются! » Это говорит о том, что мои локальные привязки неизменяемы. Если да, то как мне перенести данные в мой вектор, если это так?

Ответ №1:

Конкретная ошибка clojure.lang.Atom cannot be cast to clojure.lang.IPersistentCollection , приводящая к:

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

должно быть написано:

 (swap! determinant-vector conj e)
 

поменять местами! принимает атом в качестве первого аргумента и функцию в качестве второго аргумента. Затем он создает вызов функции, передавая текущее значение в атоме в качестве первого аргумента этой функции и после этого добавляет остальные аргументы. это будет переведено в вызов функции примерно так:

 (conj @determinant-vector e)
 

ps: @ — это макрос reader для вызова deref для получения текущего значения из одного из изменяемых типов состояний.

PPS: Чарльз Даффи прав, что цикл / повторение — гораздо лучшие инструменты для этой задачи, чем использование атома. Это должно быть примерно в 100 раз быстрее, по крайней мере, с точки зрения накладных расходов.

Комментарии:

1. Вы предлагаете мне вместо этого использовать вложенные блоки let-форм? Я в замешательстве, потому что «Локальные, созданные с помощью let, не являются переменными. После создания их значения никогда не меняются! » Это говорит о том, что мои локальные привязки неизменяемы. Если да, то как мне перенести данные в мой вектор, если это так?

2. В цикле вы передаете новую версию вектора на следующую итерацию и так далее. Каждая итерация создает ваше «изменяемое» состояние (в данном случае вектор). Это распространенный шаблон в функциональном программировании, но к нему трудно привыкнуть (по крайней мере, для меня) при использовании императивных языков.

Ответ №2:

Это не совсем отвечает на ваш вопрос, но я бы предложил использовать core.matrix для вычисления матриц / алгоритмов в Clojure. Это дает вам как минимум два больших преимущества:

  • Многие векторные / матричные операции уже реализованы. Нет особого смысла изобретать велосипед, если вам это не нужно.
  • Это позволяет вам использовать оптимизированные реализации, которые намного эффективнее, чем обычные структуры данных Clojure. Сюда входят собственные библиотеки BLAS (например, Clatrix) и быстрые библиотеки матриц pure-JVM (например, Vectorz)

Отказ от ответственности: Я сопровождаю некоторые из вышеупомянутых проектов.