映射集合而不将它们转换为列表

时间:2017-05-21 10:06:04

标签: generics collections clojure

是否可以map收藏而不将其转化为列表?我知道有关矢量的mapv,但是关于集合或地图呢。是否有一个通用的map函数将每个集合视为一个函子(即可映射的东西)并在映射后保留其类型?

以下是我的想法与现在发生的事情:

(map inc [1 2 3])         ; => [2 3 4], instead I get: (2 3 4)
(map inc #{1 2 3})        ; => #{3 2 4}, instead I get: (3 2 4)
(map inc {:a 1 :b 2 :c 3} ; => {:a 2 :b 3 :c 4}, instead I get: ClassCastException

2 个答案:

答案 0 :(得分:9)

您的功能的返回值并不完全列出,只是在打印时它们在REPL中的表示方式。它们实际上是clojure.lang.LazySeq的实例,您可以在此处看到:

(class (map inc [1 2 3]))
=> clojure.lang.LazySeq

而不是mappable,Clojure试图将所有集合处理为SequableSeq抽象允许将许多变换组合成一个集合上的操作流水线,其中维护原始输入类型通常没有意义,特别是在性能(速度)方面。在这种情况下,Clojure的动态特性实际上非常有用。

将操作输出转换为您想要的集合类型是微不足道的:

(-> (map inc [1 2 3])
    (vec))

或更简单地说,使用into

(into [] (map inc [1 2 3]))

into可用于列表,地图和集合:

(into #{} (map inc #{1 2 3}))
=> #{4 3 2}

在您尝试映射Hashmap的示例中,mapclojure.lang.MapEntry的形式传递一系列键值对,因此inc需要一个数字,不能直接处理它。

你可以做这样的事情,使用 destructuring 来取出键和值:

(into {} (for [[k v] {:a 1 :b 2 :c 3}]
           [k (inc v)]))

答案 1 :(得分:0)

into函数将任何序列转换为特定类型。要保留原始类型,请将(empty data)作为第一个参数传递:

(def data #{1 2 3})
(into (empty data) (map inc data))
;; #{4 3 2}