如何使用swap来获得clojure的结果

时间:2016-03-22 20:28:38

标签: clojure

我想编写函数collect,它可以将子列表组合成一个列表,如:

user=> (collect '(a b c d e) 1)
((a)(b)(c)(d)(e))
user=> (collect '(a b c d e) 2)
((a b)(c d)(e))
user=> (collect '(a b c d e) 5)
(a b c d e))

这是我的代码:

(defn collect [lst num]
  (loop [l lst res (atom ())]
    (if (<= (count l) num) @res
        (recur (drop num l) (swap! res conj (take num (drop num l)))))))

但是当我跑步时

user=> (collect '(a b c d e) 1)

我收到了错误:

ClassCastException clojure.lang.PersistentList cannot be cast to clojure.lang.IAtom  clojure.core/swap!

当我使用“swap”时,为什么我无法获得res? ?谢谢。

2 个答案:

答案 0 :(得分:3)

它在第二次通过循环时爆炸了。

swap返回放入原子的,而不是它自己的原子。所以第一遍是更新原子,然后将刚刚放入原子的值传递给第二次通过循环。在第二遍中,它试图将该值用作原子,并获得上述异常。

To&#34; fix&#34;这使用do更新原子,然后一旦包含正确的值,就将原子传递给下一个循环。

user> (defn collect [lst num]
        (loop [l lst res (atom ())]
          (if (<= (count l) num) @res
              (recur (drop num l) 
                     (do (swap! res conj (take num (drop num l))) 
                         res)))))
#'user/collect
user> (collect '(a b c d e) 2)
((e) (c d))

在这种情况下,您也可以完全删除原子并获得完全相同的结果(我通过在初始值中使用[]代替()来修复示例中的排序问题RES):

user> (defn collect [lst num]
        (loop [l lst res []]
          (if (<= (count l) num) res
              (recur (drop num l) 
                     (conj res (take num (drop num l)))))))
#'user/collect
user> (collect '(a b c d e) 2)
[(c d) (e)]

当然,您也可以使用上面提到的partition-all

答案 1 :(得分:1)

;; this would be a correct way to do it
(defn collect [coll n]
  (partition-all n coll))

;; this would be a clumsy way to do it
(defn collect
  "using a loop (there is not point to do that but at least you can see the logic working as in your example)"
  [coll n]
  (lazy-seq
   (loop [res []
          coll coll]
     (if (empty? coll)
       res
       (recur (conj res (take n coll)) (drop n coll))))))

关于您的错误,在第二个循环中,res是类似列表的值,而不再是atom。这将导致我们:

(defn collect [coll n]
  (lazy-seq (loop [res (atom [])
                   coll coll]
              (if (empty? coll)
                @res
                (recur (do (swap! res conj (take n coll))
                           ;; return the atom instead of the value'
                           res)
                       (drop n coll))))))

请注意,为了保留解决方案中的顺序,我使用向量(litteral [])而不是列表(litteral '())。这是因为conj described here的行为。