我对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
或filter
)都是惰性的,即使在急切的数据结构上调用也是如此。这会产生奇怪的结果,使得
(map #(* 2 %) [1 2 3])
使用惰性列表而不是向量。虽然我通常更喜欢使用惰性操作,但我发现上述内容令人困惑。事实上,向量保证了某些列表所没有的性能特征。
比如,如果我使用上述结果并在其末尾添加数据。如果我理解正确的话,结果不会被计算,直到我尝试在其上进行添加,然后它才会被计算,并返回一个列表而不是向量;因此,我必须遍历它才能在末尾添加数据。当然,之后我可以把它转换成向量,但这样会变得混乱而且可能被忽视。
如果我理解正确,`map` 是多态的,因此在向量上实现返回向量,在列表上实现返回列表,在流上实现返回流(这次采用惰性方式)等是没有问题的。我想我对 Clojure 的基本设计和惯用法有所误解。
为什么 Clojure 数据结构的基本操作不保留结构呢?
map
实现。 - Alex