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

grm*_*grm 47 clojure

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

你有conj,assocmerge,但他们似乎或多或少做同样的事情?

(assoc {:a 1 :b 2} :c 3)
(conj {:a 1 :b 2} {:c 3})
(merge {:a 1 :b 2} {:c 3})
Run Code Online (Sandbox Code Playgroud)

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

dby*_*rne 52

assoc并且conj对于其他数据结构的行为非常不同:

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]
Run Code Online (Sandbox Code Playgroud)

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

治疗merge作为唯一的地图功能(其类似于conj其他集合).

我的看法:

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

  • `merge`采用任意数量的地图并合并它们,而不仅仅是两个. (5认同)
  • 无论其他参数的类型如何,`merge`在处理`nil`(转换为`{}`)时与`conj`不同.另外,我对`assoc`与`conj`问题采用了不同的方法,但由于我已经发布了一个单独的答案,我在此不再赘述.:-) (2认同)

Mic*_*zyk 23

实际上,当与地图一起使用时,这些函数的行为完全不同.

  1. conj:

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

    请注意,seq地图包含一堆MapEntrys.因此你可以这样做

    (into a-map (filter a-predicate another-map))
    
    Run Code Online (Sandbox Code Playgroud)

    (注意在内部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
    
    Run Code Online (Sandbox Code Playgroud)

    请注意,没有任何东西(docstring除外)阻止程序员使用merge其他集合类型.如果一个人这样做,那么奇怪就会发生; 不建议.

  3. assoc:

    同样,问题文本中的例子(assoc {:a 1 :b 2} {:c 3})- 将不起作用; 相反,它会抛出一个IllegalArgumentException.assoc采用map参数后跟偶数个参数 - 奇数位置(假设地图位于0位置)是键,偶数位置是值.我发现我assoc的东西到地图往往比我conj,虽然我的时候conj,assoc会觉得累赘.;-)

  4. merge-with:

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

    (merge-with + {:a 1} {:a 3} {:a 5})
    ; => {:a 9}
    
    Run Code Online (Sandbox Code Playgroud)

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


mik*_*era 6

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

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

  • 我使用assoc为给定键和值的地图添加单个值
  • 我使用merge来组合两个地图或一次添加多个新条目
  • 我通常不会使用conj with maps,因为我精神上将它与列表相关联

  • 我认为`conj`**确实**在不同的输入类型上进行语义上等效的操作.;) (4认同)
  • 我认为惯用的方法是使用哪个函数匹配要添加到地图中的新项目最初可用的形式; 如果它是地图,使用`merge`,如果作为一组键和值未在集合中组装,请使用`assoc`等.但我真正想要指出的是,`conj`是*the*universal Clojure数据结构构建功能 - 您无法将其保留在列表抽屉中. (2认同)