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]))
([1 2] [3 4] [5 6] [7 8])
.
Spłaszczenie nie działa: daje mi (1 2 3 4 5 6 7 8)
.
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])
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)
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.
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
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])
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