lang.LazySeq无法转换为IPersistantVector

时间:2019-04-09 23:31:00

标签: clojure

在学习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)))}))

不返回向量吗?而是一个惰性序列???

但是在这一点上我迷路了。

帮助!

2 个答案:

答案 0 :(得分:3)

@amalloy有一个优点:the Clojure built-in function 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]

at the Clojure CheatSheet以及更多可用的内容。请确保将其添加为书签,并始终打开浏览器标签。

答案 1 :(得分:1)

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的调用。这是一个示例:

(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,则可以通过Java interop使用.subList()函数:

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