为什么clojure中有这么多地图构造函数?

时间:2010-07-08 12:41:00

标签: clojure

新手问题,但我真的不明白为什么在clojure中构建地图有这么多操作。

您有conjassocmerge,但他们似乎或多或少做同样的事情?

(assoc {:a 1 :b 2} :c 3)
(conj {:a 1 :b 2} {:c 3})
(merge {:a 1 :b 2} {:c 3})

真正的区别是什么?为什么所有这些方法在做或多或少相同的事情时都需要?

3 个答案:

答案 0 :(得分:51)

assocconj对其他数据结构的表现非常不同:

user=> (assoc [1 2 3 4] 1 5)
[1 5 3 4]
user=> (conj [1 2 3 4] 1 5)
[1 2 3 4 1 5]

如果你正在编写一个可以处理多种集合的函数,那么你的选择会产生很大的不同。

merge视为仅限地图的功能(与其他集合的conj类似)。

我的意见:

  • assoc - 在“更改”现有键/值对时使用
  • conj - 在“添加”新键/值对时使用
  • 合并 - 在合并两张或多张地图时使用

答案 1 :(得分:23)

实际上,当与地图一起使用时,这些功能的表现会完全不同。

  1. conj

    首先,问题文本中的(conj {:a 1 :b 2} :c 3)示例根本不起作用(无论是1.1还是1.2;抛出IllegalArgumentException)。只有少数类型可以conj映射到地图上,即双元素向量,clojure.lang.MapEntry s(基本上等同于双元素向量)和映射。

    请注意,地图的seq包含一堆MapEntry个。因此,你可以这样做。

    (into a-map (filter a-predicate another-map))
    

    (请注意into在可能的情况下使用conj - 或conj! - 内部)。 mergeassoc都不允许您这样做。

  2. merge

    这几乎完全等同于conj,但它将nil参数替换为{} - 空哈希映射 - 因此会在第一个“地图”时返回一个地图在链中碰巧是nil

    (apply conj [nil {:a 1} {:b 2}])
    ; => ({:b 2} {:a 1}) ; clojure.lang.PersistentList
    (apply merge [nil {:a 1} {:b 2}])
    ; => {:a 1 :b 2} ; clojure.lang.PersistentArrayMap
    

    请注意,除了文档字符串...之外没有任何东西可以阻止程序员将merge与其他集合类型一起使用。如果一个人这样做,那么奇怪就会发生;不推荐。

  3. assoc

    同样,问题文本中的示例 - (assoc {:a 1 :b 2} {:c 3}) - 将无效;相反,它会抛出一个IllegalArgumentExceptionassoc采用map参数后跟偶数个参数 - 奇数位置(假设地图位于0位置)是键,偶数位置是值。我发现我在地图上assoc的事情比我conj更频繁,但是当我conj时,assoc会觉得很麻烦。 ; - )

  4. merge-with

    为了完整起见,这是处理地图的最终基本功能。我发现它非常有用。它起到docstring指示的作用;这是一个例子:

    (merge-with + {:a 1} {:a 3} {:a 5})
    ; => {:a 9}
    

    请注意,如果地图包含“新”键,该键未在其左侧的任何地图中出现,则不会调用合并功能。这有时令人沮丧,但在1.2中,聪明的reify可以提供非nil“默认值”的地图。

答案 2 :(得分:6)

由于地图在Clojure中是如此普遍存在的数据结构,因此有多种工具可用于操作它们。在不同的情况下,各种不同的功能在语法上都很方便。

我个人对你提到的具体功能的看法:

  • 我使用 assoc 在给定键和值的情况下向地图添加单个值
  • 我使用合并组合两张地图或一次添加多个新条目
  • 我通常不会将 conj 与地图一起使用,因为我将其与精神上的列表相关联