新手问题,但我真的不明白为什么在clojure中构建地图有这么多操作。
您有conj
,assoc
和merge
,但他们似乎或多或少做同样的事情?
(assoc {:a 1 :b 2} :c 3)
(conj {:a 1 :b 2} {:c 3})
(merge {:a 1 :b 2} {:c 3})
真正的区别是什么?为什么所有这些方法在做或多或少相同的事情时都需要?
答案 0 :(得分:51)
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]
如果你正在编写一个可以处理多种集合的函数,那么你的选择会产生很大的不同。
将merge
视为仅限地图的功能(与其他集合的conj
类似)。
我的意见:
答案 1 :(得分:23)
实际上,当与地图一起使用时,这些功能的表现会完全不同。
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!
- 内部)。 merge
和assoc
都不允许您这样做。
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
与其他集合类型一起使用。如果一个人这样做,那么奇怪就会发生;不推荐。
assoc
:
同样,问题文本中的示例 - (assoc {:a 1 :b 2} {:c 3})
- 将无效;相反,它会抛出一个IllegalArgumentException
。 assoc
采用map参数后跟偶数个参数 - 奇数位置(假设地图位于0位置)是键,偶数位置是值。我发现我在地图上assoc
的事情比我conj
更频繁,但是当我conj
时,assoc
会觉得很麻烦。 ; - )
merge-with
:
为了完整起见,这是处理地图的最终基本功能。我发现它非常有用。它起到docstring指示的作用;这是一个例子:
(merge-with + {:a 1} {:a 3} {:a 5})
; => {:a 9}
请注意,如果地图包含“新”键,该键未在其左侧的任何地图中出现,则不会调用合并功能。这有时令人沮丧,但在1.2中,聪明的reify
可以提供非nil
“默认值”的地图。
答案 2 :(得分:6)
由于地图在Clojure中是如此普遍存在的数据结构,因此有多种工具可用于操作它们。在不同的情况下,各种不同的功能在语法上都很方便。
我个人对你提到的具体功能的看法: