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?

45
demandé sur amalloy 2010-07-08 16:41:00

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
49
répondu dbyrne 2010-07-08 17:54:11

En fait, ces fonctions se comportent différemment lorsqu'elles sont utilisées avec des maps.

  1. 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 être conjed 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 seq d'une carte comprend un tas de MapEntrys. Ainsi, vous pouvez faire par exemple

    (into a-map (filter a-predicate another-map))
    

    (notez que into utilise conj -- ou conj!, lorsque possible, à l'interne). Ni merge ni assoc ne vous permettent de le faire.

  2. merge:

    C'est presque exactement équivalent à conj, mais il remplace ses arguments nil par {} - cartes de hachage vides-et retournera donc une carte lorsque la première "carte" de la chaîne se trouve être 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
    

    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é.

  3. 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 un IllegalArgumentException. 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 je assoc les choses sur les cartes plus souvent que je conj, bien que quand je conj, assoc se sentirait encombrant. ;-)

  4. 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".

21
répondu Michał Marczyk 2010-07-08 16:43:46

É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
6
répondu mikera 2010-07-08 13:20:52