2

我正在尝试解决 4clojure.org 上设置的问题。我在 23 号(在不使用“反向”功能的情况下反转字符串)。我收到此错误:

不知道如何从:java.lang.Long 创建 ISeq

这是我的代码:

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

大概这是从试图将序列的头部到序列的其余部分的结尾。

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

4

3 回答 3

3

This error is because you are trying to cons a seq to an element rather than an element to a seq. In other words, your arguments to cons are in the wrong order. But, if you corrected the order, you'd just be piecing a list back together in the same order.

Using the same general idea you have, you could turn the second argument into a list by wrapping it in (list ...) and then concat the two lists together:

(fn rev [coll] 
  (if (empty? coll)
    coll
    (concat
      (rev (rest coll))
      (list (first coll)))))

You'll discover more concise solutions as you go along.

于 2013-02-18T01:57:56.043 回答
3

in 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 站点中的所有三个测试用例。

于 2013-02-18T01:53:59.850 回答
1

像 OP 一样,我在尝试使用when-letand ifnextandlast等构建递归解决方案时感到沮丧。所以现在我试图理解@ntalbs 的答案,看看我是否可以更进一步,研究http 上的 Clojure 文档: //clojuredocs.org/quickref/Clojure%20Core

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

在我看来,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). 我不确定这是优雅还是密集。不过,它确实可以用最少的击键来工作。

于 2013-11-14T20:00:37.280 回答