45 votes

Clojure: semi-aplatissement d'une séquence imbriquée

J'ai une liste avec des listes intégrées de vecteurs, qui ressemble à:

(([1 2]) ([3 4] [5 6]) ([7 8]))

Ce que je sais n'est pas idéal pour travailler avec. Je voudrais l'aplatir à ([1 2] [3 4] [5 6] [7 8]) .

aplatir ne fonctionne pas: cela me donne (1 2 3 4 5 6 7 8) .

Comment puis-je faire cela? Je suppose que je dois créer une nouvelle liste basée sur le contenu de chaque élément de la liste, pas sur les éléments, et c'est cette partie que je ne sais pas comment faire à partir de la documentation.

60voto

nickik Points 3112

Si vous voulez seulement l'aplatir d'un niveau, vous pouvez utiliser concat

 (apply concat '(([1 2]) ([3 4] [5 6]) ([7 8])))
=> ([1 2] [3 4] [5 6] [7 8])
 

27voto

amalloy Points 29125

Pour activer une liste de listes en une seule liste contenant les éléments de chaque sous-liste, vous souhaitez apply concat comme nickik suggère.

Cependant, il y a généralement une meilleure solution: ne pas produire la liste de listes, pour commencer! Par exemple, imaginons que vous ayez une fonction appelée get-names-for qui prend un symbole et renvoie une liste de toutes les choses cool que vous pourriez appeler ce symbole:

(get-names-for '+) => (plus add cross junction)

Si vous souhaitez obtenir tous les noms de certains de la liste des symboles, vous pouvez essayer de

(map get-names-for '[+ /]) 
=> ((plus add cross junction) (slash divide stroke))

Mais cela conduit au problème que vous aviez. Vous pourriez collez-les ensemble avec un apply concat, mais le mieux serait d'utiliser mapcat au lieu de map pour commencer:

(mapcat get-names-for '[+ /]) 
=> (plus add cross junction slash divide stroke)

8voto

David Minor Points 352

Le code pour flatten est assez court:

 (defn flatten
  [x]
  (filter (complement sequential?)
    (rest (tree-seq sequential? seq x))))
 

Il utilise tree-seq pour parcourir la structure de données et renvoyer une séquence d'atomes. Puisque nous voulons toutes les séquences de bas niveau, nous pourrions le modifier comme ceci:

 (defn almost-flatten
  [x]
  (filter #(and (sequential? %) (not-any? sequential? %))
    (rest (tree-seq #(and (sequential? %) (some sequential? %)) seq x))))
 

donc nous retournons toutes les séquences qui ne contiennent pas de séquences.

4voto

Nevena Points 467

Aussi, vous trouverez peut-être utile cette fonction générale d’aplatissement sur 1 niveau trouvée sur clojuremvc

 (flatten-1 (([1 2]) ([3 4] [5 6]) ([7 8])))
=>[[1 2] [3 4] [5 6] [7 8]]
 

concat exampe a sûrement du travail pour vous, mais ce flatten-1 autorise également les éléments non seq dans une collection:

 (flatten-1 '(1 2 ([3 4] [5 6]) ([7 8])))
=>[1 2 [3 4] [5 6] [7 8]]
;whereas 
(apply concat '(1 2 ([3 4] [5 6]) ([7 8])))
=> java.lang.IllegalArgumentException: Don't know how to create ISeq from: java.lang.Integer
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X