Lang.LazySeq нельзя привести к IPersistantVector

В процессе изучения Clojure.

У меня есть функция вытягивания случайной карты из колоды

(defn draw-random-card
  [cards]
  (let [card (rand-nth cards)
        index (.indexOf cards card)]
    {:card card :remaining-cards (concat (subvec cards 0 index)
                                         (subvec cards (inc index)))}))

Запуск:

(draw-random-card ["Ace" 2 3 4 5 6 7 8 9 10 "Jack" "Queen" "King"])
=> {:card 4, :remaining-cards ("Ace" 2 3 5 6 7 8 9 10 "Jack" "Queen" "King")}

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

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

Я бы подумал, что могу сделать что-то вроде:

(def full-deck ["Ace" 2 3 4 5 6 7 8 9 10 "Jack" "Queen" "King"])
(let [first-draw (draw-random-card full-deck)
       first-card (:drawn-card first-draw)
       second-draw (draw-random-card (:remaining-cards first-draw)) 
       second-card (:drawn-card second-draw)
       remaining-deck (:remaining-cards second-draw)]
   (println "First card: " first-card)
   (println "Second card: " second-card)
   (println "Remaining deck:" remaining-deck))

Тем не менее, я явно делаю что-то глупое, когда получаю сообщение об ошибке:

Execution error (ClassCastException) at aceyducey.core/draw-random-card (form-init3789790823166246683.clj:5).
clojure.lang.LazySeq cannot be cast to clojure.lang.IPersistentVector

думаю проблема в линии

second-draw (draw-random-card (:remaining-cards first-draw))]

Потому что оставшиеся карты — это не вектор?

Что значит

concat (subvec cards 0 index)
           (subvec cards (inc index)))}))

Разве не возвращается вектор? Скорее ленивая последовательность???

Но в этот момент я потерян.

Помощь!

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
150
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

concat возвращает ленивую последовательность. Вы можете преобразовать его в вектор, используя:

(vec (concat ...))

Вот полный код с тестом:

(ns tst.demo.core
  (:use demo.core tupelo.core tupelo.test))

(defn draw-random-card
  [cards]
  (let [card  (rand-nth cards)
        index (.indexOf cards card)]
    {:drawn-card card :remaining-cards (vec (concat (subvec cards 0 index)
                                              (subvec cards (inc index))))}))

(def full-deck ["Ace" 2 3 4 5 6 7 8 9 10 "Jack" "Queen" "King"])

(dotest
  (let [first-draw     (draw-random-card full-deck)
        first-card     (:drawn-card first-draw)
        second-draw    (draw-random-card (:remaining-cards first-draw))
        second-card    (:drawn-card second-draw)
        remaining-deck (:remaining-cards second-draw)]
    (println "First card: " first-card)
    (println "Second card: " second-card)
    (println "Remaining deck:" remaining-deck))

  )

и результат:

-------------------------------
   Clojure 1.10.0    Java 12
-------------------------------

Testing tst.demo.core
First card:  Queen
Second card:  King
Remaining deck: [Ace 2 3 4 5 6 7 8 9 10 Jack]

Обновлять:

Если быть точным, проблема заключалась в вызове subvec во 2-й итерации вашего кода. Вот пример:

(dotest
  (let [vals   (vec (range 10))     ; a vector
        s1     (subvec vals 2 4)    ; so `subvec` works
        s2     (subvec vals 6)      ; and again
        lazies (concat s1 s2)]      ; creates a lazy sez
    (is= [2 3] (spyxx s1))
    (is= [6 7 8 9] (spyxx s2))
    (is= [2 3 6 7 8 9] (spyxx lazies))
    (throws? (subvec lazies 0 2)))) ; ***** can't call `subvec` on a non-vector (lazy sequence here) *****

с результатом:

s1     => <#clojure.lang.APersistentVector$SubVector [2 3]>
s2     => <#clojure.lang.APersistentVector$SubVector [6 7 8 9]>
lazies => <#clojure.lang.LazySeq (2 3 6 7 8 9)>

поэтому, принуждая вывод concat к вектору, вызов subvec преуспевает в следующий раз через функцию.

Таким образом, оглядываясь назад, лучшим решением было бы принудить ввод к вектору следующим образом:

(let [cards   (vec cards)
      card    (rand-nth cards)
      index   (.indexOf cards card)]
  {:drawn-card card 
   :remaining-cards (vec (concat (subvec cards 0 index)
                                 (subvec cards (inc index))))}))

Обновление №2

Если вы не хотите принуждать свой ввод к vector, вы можете использовать функцию .subList() через взаимодействие Java:

(dotest
  (spyxx (.subList (concat (range 5) (range 10 15)) 5 10))
  (spyxx (.subList (range 10) 2 5))
  (spyxx (.subList (vec (range 10)) 2 5))
  (spyxx (subvec (vec (range 10)) 2 5))
  (throws? (subvec (range 10) 2 5)))   ; *** not allowed ***

с результатом

(.subList (concat (range 5) (range 10 15)) 5 10)   
        => <#java.util.ArrayList$SubList [10 11 12 13 14]>

(.subList (range 10) 2 5) 
        => <#java.util.Collections$UnmodifiableRandomAccessList [2 3 4]>

(.subList (vec (range 10)) 2 5) 
        => <#clojure.lang.APersistentVector$SubVector [2 3 4]>

(subvec (vec (range 10)) 2 5) 
        => <#clojure.lang.APersistentVector$SubVector [2 3 4]>

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

amalloy 10.04.2019 02:37
Ответ принят как подходящий

@amalloy делает хорошее замечание: встроенная функция Clojureshuffle, вероятно, то, что вам нужно:

(ns tst.demo.core
  (:use demo.core tupelo.core tupelo.test) )

(def cards [:ace 2 3 4 5 6 7 8 9 10 :jack :queen :king] )

(dotest
  (dotimes [i 3]
    (spyx (shuffle cards))))

=>

Testing tst.demo.core
(shuffle cards) => [:king :jack 6 2 9 10 :ace 4 8 5 3 :queen 7]
(shuffle cards) => [2 :jack 7 9 :queen 8 5 3 4 :ace 10 :king 6]
(shuffle cards) => [7 :queen :jack 4 3 :king 6 :ace 2 10 5 8 9]

Это и многое другое доступно в CheatSheet по Clojure. Не забудьте добавить его в закладки и всегда держать открытой вкладку браузера.

Спасибо за этот дополнительный ответ и ссылку на шпаргалку, я этого раньше не видел.

Stuart 10.04.2019 10:21

Другие вопросы по теме