像 OP 一样,我在尝试使用when-let
and if
、next
andlast
等构建递归解决方案时感到沮丧。所以现在我试图理解@ntalbs 的答案,看看我是否可以更进一步,研究http 上的 Clojure 文档: //clojuredocs.org/quickref/Clojure%20Core。
第 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)
. 我不确定这是优雅还是密集。不过,它确实可以用最少的击键来工作。