我对 Clojure 很陌生,虽然我熟悉函数式语言,主要是 Scala。
我试图弄清楚在 Clojure 中操作集合的惯用方式是什么。我对诸如map
.
在 Scala 中,非常小心地制作,以便map
始终返回与原始集合相同类型的集合,只要这是有意义的:
List(1, 2, 3) map (2 *) == List(2, 4, 6)
Set(1, 2, 3) map (2 *) == Set(2, 4, 6)
Vector(1, 2, 3) map (2 *) == Vector(2, 4, 6)
相反,据我所知,在 Clojure 中,大多数操作(例如map
or filter
)都是惰性的,即使在急切的数据结构上调用也是如此。这有一个奇怪的结果
(map #(* 2 %) [1 2 3])
惰性列表而不是向量。
虽然我通常更喜欢惰性操作,但我发现上述内容令人困惑。事实上,向量保证了某些列表不能保证的性能特征。
假设我使用上面的结果并在其末尾附加。如果我理解正确,在我尝试附加结果之前不会评估结果,然后评估它并且我得到一个列表而不是向量;所以我必须遍历它以追加到最后。当然我可以在之后把它变成一个向量,但这会变得混乱并且可以被忽略。
如果我理解正确,map
它是多态的,实现它不会是一个问题,因此它返回向量上的向量、列表上的列表、流上的流(这次是惰性语义)等等。我想我遗漏了关于 Clojure 的基本设计及其习语的一些内容。
clojure 数据结构的基本操作不影响结构的原因是什么?