我正在尝试解决4clojure.org上设置的问题。我在23号(反向字符串而不使用'反向'功能)。我收到了这个错误:
不知道如何创建ISeq:java.lang.Long
这是我的代码:
(fn rev [coll]
(if (= () coll)
nil
((cons(rev (rest coll))(first coll)))))
现在编辑到:
(fn rev [coll]
(if (empty? coll)
coll
(concat(rev (rest coll))((list first coll)))))
可能这是因为试图将序列的头部延伸到序列其余部分的末尾。
这样做的正确方法是什么?
答案 0 :(得分:3)
clojure中cons
的第二个参数应该是一个序列,但是,(first coll)
不是序列而是集合的元素。也许您传递了数字集合,因此(first coll)
吐出一个数字(长),而clojure无法从数字中创建ISeq
。
user=> (doc cons)
-------------------------
clojure.core/cons
([x seq])
Returns a new seq where x is the first element and seq is
the rest.
您可以像下面这样实现反向:
(fn rev [coll]
(reduce conj () coll))
我检查上面的代码,它在4clojure站点中传递了所有三个测试用例。
答案 1 :(得分:3)
此错误是因为您尝试将cons
seq
添加到元素而不是seq
的元素。换句话说,您对cons
的参数顺序错误。但是,如果您更正了订单,那么您只需按顺序将列表拼凑在一起。
使用与您相同的一般想法,您可以将第二个参数转换为列表,方法是将其包含在(list ...)
中,然后将concat
两个列表包装在一起:
(fn rev [coll]
(if (empty? coll)
coll
(concat
(rev (rest coll))
(list (first coll)))))
随着您的进展,您会发现更简洁的解决方案。
答案 2 :(得分:1)
与OP一样,我试图用when-let
和if
,next
和last
等构建递归解决方案感到很沮丧。所以现在我我试图理解@ntalbs的答案,看看我是否可以更进一步,在http://clojuredocs.org/quickref/Clojure%20Core研究Clojure文档。
第1部分 - 观察:
cons
是列表的函数,而conj
是集合的通用函数,concat
列在序列的“使用(修改)”下,但它返回一个惰性序列。那么这些事情在实践中有何不同?
参数顺序很重要:你不能conj
一个值和一个序列,只能一个序列和一个值。对于cons
来说,情况恰恰相反。 concat
似乎更宽容。
user=> (type (conj 1 '(2 3 4 5)))
ClassCastException java.lang.Long cannot be cast to clojure.lang.IPersistentCollection clojure.core/conj (core.clj:83)
user=> (type (cons '(2 3 4 5) 1))
IllegalArgumentException Don't know how to create ISeq from: java.lang.Long clojure.lang.RT.seqFrom (RT.java:505)
三种不同的返回类型:Cons,PersistentList或LazySeq
user=> (type (cons 1 '(2 3 4 5)))
clojure.lang.Cons
user=> (type (conj '(2 3 4 5) 1))
clojure.lang.PersistentList
user=> (type (concat 1 '(2 3 4 5)))
clojure.lang.LazySeq
不同类型的集合的不同行为:
user=> (cons 3 (sorted-set 5 7 2 7))
(3 2 5 7) ; type = Cons, 3 is just appended to the list,
user=> (conj (sorted-set 5 7 2 7) 3)
#{2 3 5 7} ; type = PersistentTreeSet, with 3 in the correct position.
user=> (concat 3 (sorted-set 5 7 2 7)) ; LazySeq can't be directly returned, so...(order doesn't matter)
IllegalArgumentException Don't know how to create ISeq from: java.lang.Long clojure.lang.RT.seqFrom (RT.java:505)
在我看来,conj
有最简单的行为,所以我会优先使用它,除非我真的想要一个懒惰的序列或特别是一个列表。
第2部分 - @ Ramy'对我来说过于密集'了解@ ntalbs的解决方案。
上面的描述表明conj
是将事物添加到集合中的最合适的方法,这正是@ntalbs的解决方案所做的。 reduce
的用法位于http://clojuredocs.org/clojure_core/clojure.core/reduce这是在收集集合中的值时应用函数的有效方法。
(reduce f val coll)
因此,从reduce
开始,f
将对集合的每个成员应用val
。调用(reduce conj () coll)
因此获取集合并首先应用(conj () (first coll))
。然后它应用(conj result (second coll))
和(conj result (third coll))
等等,其中result
是上一步的结果。
reduce
看起来像一个非常强大的命令。
第3部分 - 又一个解决方案
(fn rev [coll]
(into () coll))
从文档中,into
似乎是(reduce conj to-coll from-coll)
的语法糖。我不确定这是优雅还是只是密集。但它确实有效,只需很少的击键。