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

How to return same list by using reduce function in Clojure?

在 DrRacket 中 return 列表不通过使用 foldr 以这种方式进行更改:

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

但是在 Clojure 中,reduce 是左折叠的,我该怎么做呢?

首先我尝试了这个:

(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 如何实现 reduce。我已经知道了。这是一个更具体的问题。我自己找到了答案,我会post它。

在您的第二次尝试中,您必须 "flip" 传递给 cons 的参数才能正常工作:

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

您可以在 here

中阅读有关为什么 clojure 'lacks' foldr 的更多信息

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

  • reduce 从头到尾处理列表(相对于 foldr 从尾到头)。在这方面,reduce 类似于 foldl
  • reduce 将累加值作为 first 参数传递给缩减函数(相对于 last 参数 foldr/foldl).

第二个区别是使用cons时出错的原因——(cons '() 1)是第一次调用,而1显然不是列表。

如果我们认为当xs是一个列表时(conj xs x)等价于(cons x 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 输入列表:

(reduce conj () (reverse '(1 2 3)))

这是我的解决方案。我不知道它的效率如何。我们在大学的 Racket 课程中使用了这种解决方案。

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