Why are there so many map construction functions in clojure?
Novice question, but I don't really understand why there are so many operations for constructing maps in clojure.
You have conj, assoc and merge, but they see开发者_高级运维m to more or less do the same thing?
(assoc {:a 1 :b 2} :c 3)
(conj {:a 1 :b 2} {:c 3})
(merge {:a 1 :b 2} {:c 3})
What's really the difference and why are all these methods required when they do more or less the same thing?
assoc and conj behave very differently for other data structures:
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]
If you are writing a function that can handle multiple kinds of collections, then your choice will make a big difference.
Treat merge as a maps-only function (its similar to conj for other collections).
My opinion:
- assoc - use when you are 'changing' existing key/value pairs
- conj - use when you are 'adding' new key/value pairs
- merge - use when you are combining two or more maps
Actually these functions behave quite differently when used with maps.
conj:Firstly, the
(conj {:a 1 :b 2} :c 3)example from the question text does not work at all (neither with 1.1 nor with 1.2;IllegalArgumentExceptionis thrown). There are just a handful of types which can beconjed onto maps, namely two-element vectors,clojure.lang.MapEntrys (which are basically equivalent to two-element vectors) and maps.Note that
seqof a map comprises a bunch ofMapEntrys. Thus you can do e.g.(into a-map (filter a-predicate another-map))(note that
intousesconj-- orconj!, when possible -- internally). Neithermergenorassocallows you to do that.merge:This is almost exactly equivalent to
conj, but it replaces itsnilarguments with{}-- empty hash maps -- and thus will return a map when the first "map" in the chain happens to benil.(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.PersistentArrayMapNote there's nothing (except the docstring...) to stop the programmer from using
mergewith other collection types. If one does that, weirdness ensues; not recommended.assoc:Again, the example from the question text --
(assoc {:a 1 :b 2} {:c 3})-- won't work; instead, it'll throw anIllegalArgumentException.assoctakes a map argument followed by an even number of arguments -- those in odd positions (let's say the map is at position 0) are keys, those at even positions are values. I find that Iassocthings onto maps more often than Iconj, though when Iconj,assocwould feel cumbersome. ;-)merge-with:For the sake of completeness, this is the final basic function dealing with maps. I find it extremely useful. It works as the docstring indicates; here's an example:
(merge-with + {:a 1} {:a 3} {:a 5}) ; => {:a 9}Note that if a map contains a "new" key, which hasn't occured in any of the maps to the left of it, the merging function will not be called. This is occasionally frustrating, but in 1.2 a clever
reifycan provide a map with non-nil"default values".
Since maps are such a ubiquitous data structure in Clojure, it makes sense to have multiple tools for manipulating them. The various different functions are all syntactically convenient in slightly different circumstances.
My personal take on the specific functions you mention :
- I use assoc to add a single value to a map given a key and value
- I use merge to combine two maps or add multiple new entries at once
- I don't generally use conj with maps at all as I associate it mentally with lists
加载中,请稍侯......
精彩评论