Pourquoi y a-t-il tant de fonctions de construction de cartes dans clojure?
Question Novice, mais je ne comprends pas vraiment pourquoi il y a tant d'opérations pour construire des cartes dans clojure.
Vous avez conj, assoc et merge, mais ils semblent plus ou moins faire la même chose?
(assoc {:a 1 :b 2} :c 3)
(conj {:a 1 :b 2} {:c 3})
(merge {:a 1 :b 2} {:c 3})
Quelle est vraiment la différence et pourquoi toutes ces méthodes sont-elles nécessaires quand elles font plus ou moins la même chose?
3 réponses
assoc et conj se comportent très différemment pour d'autres structures de données:
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]
Si vous écrivez une fonction qui peut gérer plusieurs types de collections, votre choix fera une grande différence.
Traitez merge comme une fonction maps uniquement (c'est similaire à conj pour les autres collections).
Mon avis:
- assoc - utiliser lorsque vous 'modifiez' les paires clés/valeurs existantes
- conj utiliser lorsque vous êtes "ajout" clé/valeur paires
- merge - utiliser lorsque vous combinez deux cartes ou plus
En fait, ces fonctions se comportent différemment lorsqu'elles sont utilisées avec des maps.
-
conj:Tout d'abord, l'exemple
(conj {:a 1 :b 2} :c 3)du texte de la question ne fonctionne pas du tout (ni avec 1.1 ni avec 1.2;IllegalArgumentExceptionest lancé). Il y a juste une poignée de types qui peuvent êtreconjed sur des cartes, à savoir des vecteurs à deux éléments,clojure.lang.MapEntrys (qui sont fondamentalement équivalents à des vecteurs à deux éléments) et des cartes.Remarque que
seqd'une carte comprend un tas deMapEntrys. Ainsi, vous pouvez faire par exemple(into a-map (filter a-predicate another-map))(notez que
intoutiliseconj-- ouconj!, lorsque possible, à l'interne). Nimergeniassocne vous permettent de le faire. -
merge:C'est presque exactement équivalent à
conj, mais il remplace ses argumentsnilpar{}- cartes de hachage vides-et retournera donc une carte lorsque la première "carte" de la chaîne se trouve êtrenil.(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.PersistentArrayMapNotez qu'il n'y a rien (sauf le docstring... pour arrêter le programmeur de l'aide
mergeavec d'autres types de collection. Si l'on fait cela, l'étrangeté s'ensuit; non recommandé. -
assoc:Encore une fois, l'exemple du texte de la question -
(assoc {:a 1 :b 2} {:c 3})- ne fonctionnera pas; à la place, il lancera unIllegalArgumentException.assocprend un argument map suivi d'un nombre pair d'arguments-ceux dans des positions impaires (disons que la carte est à la position 0) sont des clés, ceux à des positions paires sont des valeurs. Je trouve que jeassocles choses sur les cartes plus souvent que jeconj, bien que quand jeconj,assocse sentirait encombrant. ;-) -
merge-with:Par souci d'exhaustivité, c'est la fonction de base finale traitant des cartes. Je trouve cela extrêmement utile. Cela fonctionne comme l'indique docstring; voici un exemple:
(merge-with + {:a 1} {:a 3} {:a 5}) ; => {:a 9}Notez que si une carte contient une "nouvelle" clé, qui ne s'est produite dans aucune des cartes à gauche de celle-ci, la fonction de fusion ne sera pas appelée. C'est parfois frustrant, mais en 1.2 un savant
reifypeut fournir une carte avec non-nil"valeurs par défaut".
Étant donné que les cartes sont une structure de données omniprésente dans Clojure, il est logique d'avoir plusieurs outils pour les manipuler. Les différentes fonctions sont toutes syntaxiquement pratiques dans des circonstances légèrement différentes.
Mon point de vue personnel sur les fonctions spécifiques que vous mentionnez :
- j'utilise assoc pour ajouter une valeur unique pour une carte donnée, une clé et une valeur
- j'utilise fusion pour combiner deux cartes ou ajouter plusieurs nouvelles entrées à la fois
- Je ne sais pas utilisez généralement conj avec des cartes car je l'associe mentalement avec des listes