Переходные процессы Clojure - доц. вызывающее исключение

Вот функция, которую я пытаюсь запустить...

(defn mongean [cards times]
  (let [_cards (transient cards)]
    (loop [i 0 c (get cards i) _count (count cards) _current (/ _count 2)]
      (assoc! _cards _current c)
      (if ((rem i 2) = 0)
        (def _newcur (- _current (inc i)))
        (def _newcur (+ _current (inc i))))
      (if (<= i _count)
        (recur (inc i) (get cards i) _count _newcur )))
    (persistent! _cards)))

Это приводит к этому исключению...

Exception in thread "main" java.lang.ClassCastException: clojure.lang.PersistentHashSet$TransientHashSet cannot be cast to clojure.lang.ITransientAssociative

Будучи новичком в clojure, я также был бы признателен за любую конструктивную критику моего подхода выше. Цель состоит в том, чтобы взять список и вернуть переупорядоченный список.


person jondavidjohn    schedule 24.11.2012    source источник
comment
Что вы подразумеваете под переупорядоченным списком?   -  person Ankur    schedule 24.11.2012


Ответы (2)


Я предполагаю, что вы пытаетесь реализовать тасовку монгойского. Ваш подход очень императивен, и вы должны попытаться использовать более функциональный подход.

Это была бы возможная реализация, если бы мы вычислили окончательный порядок карточек (согласно формуле Википедии), а затем использовали встроенную функцию replace для сопоставления:

 (defn mongean [cards]
   (let [num-cards (count cards)
         final-order (concat (reverse (range 1 num-cards 2)) (range 0 num-cards 2))]
      (replace cards final-order)))

  user> (mongean [1 2 3 4 5 6 7 8])
  (8 6 4 2 1 3 5 7)
person DanLebrero    schedule 24.11.2012
comment
действительно умный... clojure постоянно заставляет меня чувствовать себя идиотом... Спасибо! - person jondavidjohn; 24.11.2012
comment
Что вы имеете в виду, говоря, что ваш подход очень императивен, а также есть ли какие-либо золотые ресурсы для тех, кто не использует функциональный стиль, исходя из классического фона OO? Спасибо еще раз. - person jondavidjohn; 25.11.2012
comment
Когда я начал играть с Clojure, мне очень не хватало классического императивного цикла for. Как вы можете что-то делать без этого? :). Один из столпов функционального программирования — не использовать изменяющееся состояние. Если вы используете transient, вы сразу говорите, что мне нужно изменяемое состояние!. transient следует зарезервировать для разделов кода, где вам действительно нужно повысить производительность. О том, какие ресурсы, я читал только книги по Clojure, но ищу SO, так как есть несколько вопросов о том, как научиться программированию func - person DanLebrero; 25.11.2012

Как вы называете эту функцию? Похоже, вы передаете набор, поэтому его переходная версия также будет набором и, следовательно, не может использоваться ни с одной из функций assoc, поскольку они работают с ассоциативными структурами данных и векторами:

user=> (assoc #{} :a 1)
ClassCastException clojure.lang.PersistentHashSet cannot be cast to clojure.lang.Associative  clojure.lang.RT.assoc (RT.java:691)

user=> (assoc! (transient #{}) :a 1)
ClassCastException clojure.lang.PersistentHashSet$TransientHashSet cannot be cast to clojure.lang.ITransientAssociative  clojure.core/assoc! (core.clj:2959)

; the following works as it uses maps and vectors
user=> (assoc {} :a 1)
{:a 1}
user=> (assoc! (transient {}) :a 1)
#<TransientArrayMap clojure.lang.PersistentArrayMap$TransientArrayMap@65cd1dff>
user=> (assoc [] 0 :a)
[:a]

Теперь давайте попробуем обсудить сам код. Немного сложно следить за вашим кодом и пытаться понять, какова на самом деле цель, без дополнительных намеков на то, чего вы хотите достичь, но в виде общих комментариев:

  • у вас есть входной параметр times, который вы вообще не используете

  • вы должны использовать результат временной мутации, а не предполагать, что переходный процесс будет мутировать на месте

  • избегайте переходных процессов, если можете, они предназначены только для оптимизации производительности

  • привязка _current (/ _count 2), вероятно, не то, что вы хотите, так как (/ 5 2) действительно возвращает 5/2 и кажется, что вы хотите использовать его как позицию в результате

  • такие константы, как _count, не обязательно должны быть частью привязки loop, вы можете использовать внешнюю let, чтобы вам не приходилось передавать их на каждой итерации

  • используйте let вместо def для именования вещей внутри функции

  • (if ((rem 1 2) = 0)) определенно не то, что вам нужно

Теперь, оставив в стороне алгоритм перетасовки, если вам нужно изменить последовательность, вы можете просто создать последовательность новых позиций, map их с исходными картами, чтобы получить пары [position card] и, наконец, reduce их, поместив карту в новую позицию, используя исходная последовательность в качестве семени:

(defn generate [coll] ; counts down from (count coll) to 0, change to
                      ; implement your shuffling algorithm
  (range (dec (count coll)) -1 -1))

(defn mongean [cards times]
  (let [positions (generate cards) ; get the new positions
        assemble (fn [dest [pos card]] ; assoc the card at the wanted position
                   (assoc dest pos card))]
    (reduce assemble cards (map vector positions cards))))

Если вы просто хотите перетасовать:

(defn mongean [cards times] (shuffle cards))
person skuro    schedule 24.11.2012