web-dev-qa-db-fra.com

Mettre un élément à la queue d'une collection

Je me retrouve à faire beaucoup de:

(concat coll [e]) où coll est une collection et e un seul élément.

Y a-t-il une fonction pour faire cela dans Clojure? Je sais que conj fait le mieux pour les vecteurs, mais je ne sais pas d'avance quel coll sera utilisé. Il peut s'agir d'un vecteur, d'une liste ou d'un ensemble trié par exemple.

51
Michiel Borkent

Certains types de collections peuvent s'ajouter à moindre coût à l'avant (listes, seq), tandis que d'autres peuvent s'ajouter à moindre coût à l'arrière (vecteurs, files d'attente, kinda-sorta lazy-seqs). Plutôt que d'utiliser concat, si possible, vous devriez vous arranger pour travailler avec l'un de ces types (le vecteur est le plus courant) et juste le conj: (conj [1 2 3] 4) donne [1 2 3 4], tandis que (conj '(1 2 3) 4) donne (4 1 2 3).

52
amalloy

concat n'ajoute pas d'élément à la fin d'une collection, ni ne concatène deux collections.

concat renvoie une séquence composée de la concaténation de deux autres séquences. Le type d'origine des collections à partir desquelles les séquences peuvent être déduites sont perdues pour le type de retour de concat.

Maintenant, les collections clojure ont différentes propriétés que l'on doit connaître pour écrire du code efficace, c'est pourquoi il n'y a pas de fonction universelle disponible dans le noyau pour concaténer des collections de toute sorte ensemble. Au contraire, la liste et les vecteurs ont des "positions d'insertion naturelles" que conj connaît et fait ce qui est bon pour le type de collection.

14
Laurent Petit

Il s'agit d'un très petit addendum à la réponse de @ amalloy afin de répondre à la demande d'OP pour une fonction qui ajoute toujours à la queue de tout type de collection. Ceci est une alternative à (concat coll [x]). Créez simplement une version vectorielle de la collection originale:

(defn conj*
  [s x]
  (conj (vec s) x))

Mises en garde:

Si vous avez commencé avec une séquence paresseuse, vous avez maintenant détruit la paresse - c'est-à-dire. la sortie n'est pas paresseuse. Cela peut être une bonne ou une mauvaise chose, selon vos besoins.

La création du vecteur coûte cher. Si vous devez souvent appeler cette fonction et que vous trouvez (par exemple en comparant avec Criterium) que ce coût est important pour vos besoins, suivez les conseils des autres réponses pour essayer d'utiliser des vecteurs en premier lieu.

6
Mars

Pour distiller le meilleur de ce qu'ont déjà dit amalloy et Laurent Petit: tilisez la fonction conj.

L'une des grandes abstractions fournies par Clojure est l'API de séquence, qui inclut la fonction conj. Dans la mesure du possible, votre code doit être aussi indépendant du type de collection qu'il peut l'être, utilisant plutôt l'API seq pour gérer les opérations sur les collections et choisir un type de collection particulier uniquement lorsque vous devez être spécifique.

Si les vecteurs sont une bonne correspondance, alors oui, conj ajoutera des éléments à la fin. Si vous utilisez plutôt des listes, alors conj ajoutera des éléments au début de votre collection. Mais si vous utilisez ensuite les fonctions API seq standard pour extraire des éléments du "haut" d'une collection (le dos d'un vecteur, le début d'une liste), peu importe l'implémentation que vous utilisez, car elle utilisera toujours celui avec les meilleures performances et donc ajouter et supprimer des éléments sera cohérent.

5
semperos