如何在Clojure中使用reduce函数返回相同的列表?

时间:2016-05-04 00:44:20

标签: clojure scheme racket reduce fold

在DrRacket中返回列表而不改变使用foldr这样做:

(foldr cons '() '(1 2 3))

然而在Clojure中,减少是折叠,所以我该怎么做?

首先我尝试了这个:

(reduce cons '() '(1 2 3))

=> IllegalArgumentException Don't know how to create ISeq from: java.lang.Long  clojure.lang.RT.seqFrom (RT.java:542)

之后我尝试了这个:

(reduce conj '() '(1 2 3))
=> (3 2 1)

“=>” 中是REPL中的输出

不,我不想读Clojure如何实现减少。我已经知道了。这是一个更具体的问题。我自己找到了答案,我会发布。

3 个答案:

答案 0 :(得分:3)

在你的第二次尝试中,你必须"翻转"传递给利弊的论据:

(reduce #(cons %2 %1) '() '(1 2 3))
=> (3 2 1)

但是,正如您所注意到的,reduce实际上是一个向左折叠,因此原始列表中的第一个项目成为结果列表中最内部(或最后)的项目。这可以通过reverse处理:

(reduce (fn[a b](cons b a)) '() (reverse '(1 2 3)))
=> (1 2 3)

您可以详细了解为什么clojure缺乏' foldr in here

答案 1 :(得分:0)

我对Racket并不熟悉,但Clojure的reduce似乎与两个主要方面的Racket foldr不同:

  • reduce从头到尾处理列表(与foldr的尾部相对)。在这方面,reduce类似于foldl
  • reduce将累计值作为 first 参数传递给reduce函数(与foldr / {{1的 last 参数相对}})。

第二个区别是使用foldl时出错的原因 - cons是第一次拨打电话,(cons '() 1)显然不是列表。

如果我们认为当1是列表时(conj xs x)等同于(cons x xs),那么xs相当于(reduce conj '() '(1 2 3)),这可能会更加明显写成

(cons 3 (cons 2 (cons 1 '())))

现在,如果你不介意结果是向量而不是列表,你可以这样做:

(->> '()
     (cons 1)
     (cons 2)
     (cons 3))

或者,如果您愿意,可以将结果转换为(reduce conj [] '(1 2 3)) ,以便它基本上像列表一样:

seq

或者,您可以(seq (reduce conj [] '(1 2 3))) 输入列表:

reverse

答案 2 :(得分:0)

这是我的解决方案。我不知道它有多高效。在Racket课程中我们在大学里使用过这种解决方案。

(reduce #(concat %1 (list %2)) '() '(1 2 3))
=> (1 2 3)