Clojure: pół-spłaszczenie sekwencji zagnieżdżonej

Mam listę z wbudowanymi listami wektorów, która wygląda następująco:

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

Wiem, że nie nadaje się do pracy. Chciałbym to spłaszczyć ([1 2] [3 4] [5 6] [7 8]).

Spłaszczenie nie działa: daje mi (1 2 3 4 5 6 7 8).

Jak mam to zrobić? Myślę, że muszę utworzyć nową listę na podstawie zawartości każdego elementu listy, a nie elementów, a to jest ta część, której nie mogę dowiedzieć się, jak to zrobić z dokumentów.
Author: amalloy, 2011-03-08

5 answers

Jeśli chcesz spłaszczyć go tylko o jeden poziom możesz użyć concat

(apply concat '(([1 2]) ([3 4] [5 6]) ([7 8])))
=> ([1 2] [3 4] [5 6] [7 8])
 59
Author: nickik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-08 12:58:47

Aby zamienić listę list w pojedynczą listę zawierającą elementy każdej podlisty, chcesz apply concat Jak sugeruje nickik.

Jednak, zazwyczaj jest lepsze rozwiązanie: nie produkować listy-of-list na początek! Na przykład, wyobraźmy sobie, że masz funkcję o nazwie get-names-for, która pobiera symbol i zwraca listę wszystkich fajnych rzeczy, które możesz nazwać tym symbolem:

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

Jeśli chcesz uzyskać wszystkie nazwy niektórych symboli, możesz spróbować

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

Ale to to prowadzi do twojego problemu. Można je skleić za pomocą apply concat, ale lepiej byłoby użyć mapcat zamiast map na początek:

(mapcat get-names-for '[+ /]) 
=> (plus add cross junction slash divide stroke)
 27
Author: amalloy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-08 17:41:30

Kod dla {[2] } jest dość krótki:

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

Używa tree-seq do przechodzenia przez strukturę danych i zwracania sekwencji atomów. Ponieważ chcemy wszystkie sekwencje dolnego poziomu, możemy zmodyfikować je w następujący sposób:

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

Więc zwracamy wszystkie sekwencje, które nie zawierają sekwencji.

 8
Author: David Minor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-08 18:47:40

Również może okazać się przydatna ta ogólna funkcja spłaszczania poziomu 1, którą znalazłem na 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)))))

Przykład:

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

concat Przykład na pewno działa za ciebie, ale to flatten-1 jest również pozwalając elementom non seq wewnątrz zbioru:

(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
 4
Author: Nevena,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-03 09:47:07

Oto funkcja, która spłaszczy się do poziomu sekwencji, niezależnie od nierównomiernego zagnieżdżania:

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

Więc jeśli twoja oryginalna Sekwencja brzmiała:

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

Nadal otrzymałbyś ten sam wynik:

([1 2] [3 4] [5 6] [7 8])
 2
Author: bazeblackwood,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-09 19:31:44