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;IllegalArgumentException
est lancé). Il y a juste une poignée de types qui peuvent êtreconj
ed sur des cartes, à savoir des vecteurs à deux éléments,clojure.lang.MapEntry
s (qui sont fondamentalement équivalents à des vecteurs à deux éléments) et des cartes.Remarque que
seq
d'une carte comprend un tas deMapEntry
s. Ainsi, vous pouvez faire par exemple(into a-map (filter a-predicate another-map))
(notez que
into
utiliseconj
-- ouconj!
, lorsque possible, à l'interne). Nimerge
niassoc
ne vous permettent de le faire. -
merge
:C'est presque exactement équivalent à
conj
, mais il remplace ses argumentsnil
par{}
- 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.PersistentArrayMap
Notez qu'il n'y a rien (sauf le docstring... pour arrêter le programmeur de l'aide
merge
avec 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
.assoc
prend 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 jeassoc
les choses sur les cartes plus souvent que jeconj
, bien que quand jeconj
,assoc
se 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
reify
peut 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