如何在 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)
在 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)