lang.LazySeq не может быть приведен к IPersistantVector - PullRequest
0 голосов
/ 10 апреля 2019

В процессе изучения 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")}

IЯ хотел бы вызвать его дважды и получить 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)))}))

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

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

Помогите!

Ответы [ 2 ]

3 голосов
/ 10 апреля 2019

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

(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]

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

1 голос
/ 10 апреля 2019

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))))}))

Update # 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]>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...