我们如何在Clojure中左右折叠?

减less工作正常,但更像是折叠。 还有什么其他forms的减less让我折叠到正确的?

clojure标准库只有fold-left(reduce)的原因实际上是非常微妙的,这是因为clojure没有足够的懒惰来获得fold-right的主要好处。

在haskell这样的语言中,折叠权的主要好处是可以实际上短路。 如果我们以foldr (&&) True [False, True, True, True, True]进行foldr (&&) True [False, True, True, True, True] ,那么这个方法非常有启发性。 它唯一需要评估的是函数and 1个参数(第一个False )。 一旦它到达那里,它就知道答案,并且不需要评估任何的True

如果你仔细看照片:

在这里输入图像说明

你会看到,尽pipe从概念上讲,折叠开始和列表的末尾并向前移动,实际上,它开始在列表的前面进行评估。

这是懒惰/咖喱函数和尾recursion可以给clojure不能的好处的一个例子。

奖金部分(对于那些感兴趣的人)

基于vemv的推荐,我想提一下,Clojure在核心命名空间中增加了一个新的function来克服这个限制,即Clojure不能有懒惰的折叠。 在核心命名空间中有一个叫reduce的函数,可以让你使Clojure的reduce 。 通过告诉它不要查看列表的其余部分,可以用它来缩短reduce时间。 例如,如果想要乘以数字列表,但有理由怀疑列表偶尔会包含零,并且希望将其作为特殊情况处理,因为一旦遇到零时不查看列表的其余部分,您可以编写下面的multiply-all函数(注意使用reduced来表示最终答案是0而不pipe列表的其余部分是什么)。

 (defn multiply-all [coll] (reduce (fn [accumulator next-value] (if (zero? next-value) (reduced 0) (* accumulator next-value))) 1 coll)) 

然后为了certificate它短路,你可以乘以一个无限的数字列表,它恰好包含一个零,并且看到它确实以0

 (multiply-all (cycle [1 2 3 4 0])) 

我们来看看每个可能的定义:

 (defn foldl [f val coll] (if (empty? coll) val (foldl f (f val (first coll)) (rest coll)))) (defn foldr [f val coll] (if (empty? coll) val (f (foldr f val (rest coll)) (first coll)))) 

请注意,只有foldl处于尾部位置,recursion调用可以由recur 。 所以recurfoldl将不会占用堆栈空间,而foldr会。 这就是为什么reduce就像foldl一样。 现在让我们试试看:

 (foldl + 0 [1 2 3]) ;6 (foldl - 0 [1 2 3]) ;-6 (foldl conj [] [1 2 3]) ;[1 2 3] (foldl conj '() [1 2 3]) ;(3 2 1) (foldr + 0 [1 2 3]) ;6 (foldr - 0 [1 2 3]) ;-6 (foldr conj [] [1 2 3]) ;[3 2 1] (foldr conj '() [1 2 3]) ;(1 2 3) 

你有什么理由要折叠吗? 我认为foldr最常用的用法是从前到后列出一个列表。 在Clojure中,我们不需要这个,因为我们可以使用一个向量。 避免堆栈溢出的另一种select是使用惰性序列:

 (defn make-list [coll] (lazy-seq (cons (first coll) (rest coll)))) 

所以,如果你想正确的select一些有效的select

  1. 使用一个vector。
  2. 使用一个懒惰的序列。
  3. 使用reduced到短路reduce
  4. 如果你真的想潜入一个兔子洞,使用一个传感器。