在Clojure中没有标准库的反转序列

时间:2013-02-18 01:28:22

标签: clojure reverse

我正在尝试解决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)))))

可能这是因为试图将序列的头部延伸到序列其余部分的末尾。

这样做的正确方法是什么?

3 个答案:

答案 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-letifnextlast等构建递归解决方案感到很沮丧。所以现在我我试图理解@ntalbs的答案,看看我是否可以更进一步,在http://clojuredocs.org/quickref/Clojure%20Core研究Clojure文档。

第1部分 - 观察:
cons是列表的函数,而conj是集合的通用函数,concat列在序列的“使用(修改)”下,但它返回一个惰性序列。那么这些事情在实践中有何不同?

  1. 参数顺序很重要:你不能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)
    
  2. 三种不同的返回类型: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
    
  3. 不同类型的集合的不同行为:

    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)
    
  4. 在我看来,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)的语法糖。我不确定这是优雅还是只是密集。但它确实有效,只需很少的击键。