#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)
Отказ от ответственности: Я сопровождаю некоторые из вышеупомянутых проектов.