2011-03-08 25 views
44

Tengo una lista con las listas incrustadas de vectores, que se parece:Clojure: Semi-Aplanar una secuencia anidada

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

que sé que no es lo ideal para trabajar. Me gustaría aplanar esto al ([1 2] [3 4] [5 6] [7 8]).

aplanar no funciona: me da (1 2 3 4 5 6 7 8).

¿Cómo puedo hacer esto? Me imagino que necesito crear una nueva lista basada en el contenido de cada elemento de la lista, no los elementos, y esta es la parte que no puedo encontrar como hacer desde los documentos.

Respuesta

59

Si sólo desea para aplanarlo un nivel que puede utilizar concat

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

para convertir una lista de lista de en una sola lista que contiene los elementos de cada sub-lista, que desea apply concat como nickik sugiere.

Sin embargo, usualmente hay una mejor solución: ¡para empezar, no elabore la lista de listas! Por ejemplo, imaginemos que tiene una función llamada get-names-for que tiene un símbolo y devuelve una lista de todas las cosas interesantes que usted podría llamar a ese símbolo:

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

Si desea obtener todos los nombres por alguna lista de símbolos , puede intentar

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

Pero esto lleva al problema que tenía. Se podría unirlos con una apply concat, pero mejor sería utilizar mapcat en lugar de map, para empezar:

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

Esta debería ser la respuesta aceptada IMO. – ehsanul

+1

sí +1 para mapcat - esto llevó a una solución MUCHO más elegante de la que hubiera tenido de otra manera. muy bueno saber ¡Gracias! – jm0

8

El código para flatten es bastante corta:

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

Se utiliza tree-seq caminar a través de la estructura de datos y devolver una secuencia de los átomos. Como queremos que todas las secuencias de nivel inferior, podríamos modificarlo así:

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

así que devolver todas las secuencias que no contienen secuencias.

4

También es posible que se encuentra presente en general 1 nivel aplanar útil función que encontré en clojuremvc:

(defn flatten-1 
    "Flattens only the first level of a given sequence, e.g. [[1 2][3]] becomes 
    [1 2 3], but [[1 [2]] [3]] becomes [1 [2] 3]." 
    [seq] 
    (if (or (not (seqable? seq)) (nil? seq)) 
    seq ; if seq is nil or not a sequence, don't do anything 
    (loop [acc [] [elt & others] seq] 
     (if (nil? elt) acc 
     (recur 
      (if (seqable? elt) 
      (apply conj acc elt) ; if elt is a sequence, add each element of elt 
      (conj acc elt))  ; if elt is not a sequence, add elt itself 
     others))))) 

Ejemplo:

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

concat exampe sin duda hacer el trabajo para usted, pero esto flatten-1 es también permitiendo elementos no seq. dentro de una colección:

(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 
2

Aquí es una función que se aplanan hasta el nivel de secuencia, independientemente de anidación desigual:

(fn flt [s] (mapcat #(if (every? coll? %) (flt %) (list %)) s)) 

Así que si su secuencia original:

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

Seguimos obteniendo el mismo resultado:

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