为什么clojure中有这么多映射构造函数?
新手的问题,但我真的不明白为什么在clojure中有这么多构建地图的操作 您有为什么clojure中有这么多映射构造函数?,clojure,Clojure,新手的问题,但我真的不明白为什么在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}) 真正的区别是什么?为什么当它们做或多或少相同的事情时需要所有这些方法 由于地图在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})
真正的区别是什么?为什么当它们做或多或少相同的事情时需要所有这些方法 由于地图在Clojure中是如此普遍的数据结构,因此有多种工具来操作它们是有意义的。在稍微不同的情况下,各种不同的功能在语法上都很方便 我个人对您提到的具体功能的看法:
- 我使用assoc向给定键和值的映射添加单个值
- 我使用合并合并两个地图或一次添加多个新条目
- 我根本不把conj用于地图,因为我在心里把它与列表联系起来
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
)
我的意见是:
- 关联-在“更改”现有键/值对时使用
- conj-在“添加”新键/值对时使用
- 合并-在合并两个或多个地图时使用
conj
:
首先,问题文本中的(conj{:a1:b2}:c3)
示例根本不起作用(1.1和1.2都不起作用;illegargumentexception
)。只有少数类型可以连接到地图上,即两个元素向量,clojure.lang.MapEntry
s(基本上相当于两个元素向量)和地图
请注意,地图的seq
由一组MapEntry
s组成。因此,你可以这样做
(into a-map (filter a-predicate another-map))
(请注意,into
尽可能在内部使用conj
——或conj!
)。无论是merge
还是assoc
都不允许您这样做合并
:
这几乎完全等同于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
注意,没有任何东西(除了docstring…)可以阻止程序员将merge
与其他集合类型一起使用。如果一个人这样做了,奇怪就会接踵而至;不推荐assoc
:
同样,问题文本中的示例--(assoc{:a1:b2}{:c3})
--将不起作用;相反,它将抛出一个非法argumentException
assoc
接受一个map参数,后跟偶数个参数——奇数位置的参数(假设map位于位置0)是键,偶数位置的参数是值。我发现我将事物关联到地图上的次数比我将事物关联到地图上的次数要多,尽管当我将事物关联到地图上时,assoc
会觉得很麻烦与
合并:
为了完整起见,这是处理映射的最后一个基本函数。我觉得它非常有用。它按照docstring的指示工作;下面是一个例子:
(merge-with + {:a 1} {:a 3} {:a 5})
; => {:a 9}
请注意,如果一个映射包含一个“new”键,而该键在它左边的任何映射中都没有出现,则不会调用merging函数。这有时令人沮丧,但在1.2中,一个聪明的具体化可以提供一个带有非nil
默认值的映射
merge
获取任意数量的映射并将它们合并,而不仅仅是两个。merge
与conj
在处理nil
(转换为{}
)时有所不同,不管其他参数的类型如何。此外,我对assoc
与conj
问题有不同的处理方法,但由于我已经发布了一个单独的答案,我将不在此详述。:-)conj还可以处理任意数量的贴图。user=>(conj{:a1:b2}{:c3}{:d4}{:d4,:c3,:a1,:b2}如何将合并到
中?为了说明与
合并的最后一点,我准备了以下要点:感谢您的解释。正如您所看到的,我出现了一个键入错误,并在conj和assoc之间切换了第二个参数。我认为惯用的方法是使用与要添加到映射中的新项最初可用的形式相匹配的函数;如果是作为映射,请使用merge
,如果是作为未在集合中组合的一组键和值,请使用assoc
等。不过,我真正想指出的是,那就是conj
是通用的Clojure数据结构构建函数——你不能真的把它放在列表抽屉里。@Michal也许你是对的——但是出于某种原因,我不相信在不同的输入类型上做语义上非常不同的事情而没有警告的函数:-)我认为conj
确实可以不同输入类型上的语义等价操作。;)还有(进入{:a1:b2}{:c3})