我如何在 Clojure 中递归地展平任意嵌套的向量和地图?
how can I recursively flatten arbitrarily nested vectors and maps in Clojure?
我正在尝试使用递归遍历 Clojure 中任意嵌套的向量和映射树,以及 return 仅包含关键字的向量,包括顶部。
所以下面的样本数据应该return:
[:top :top :top :top :top :top :top :top :bottom :bottom :bottom :bottom :bottom :bottom :bottom :bottom :bottom :bottom :bottom :bottom]
,
但排名不分先后。
有人可以帮我正确地做到这一点吗?以下是我目前所拥有的。
(def sample [{:top {:top {:top [:bottom {:top {:top [:bottom :bottom :bottom]}} :bottom :bottom :bottom]}}},
{:top {:top [:bottom :bottom :bottom]}},
{:top [:bottom :bottom]}])
(defn make-flat [graph]
(loop [graph graph]
(if (every? keyword? graph) graph
(recur (into graph (flatten (seq (first (filter #(not (keyword? %)) graph)))))))))
(make-flat sample)
我怀疑 clojure.clj = clojure 基础库中已经有一个函数可以执行此操作
果然有
https://clojuredocs.org/clojure.core/flatten
但是,如果您这样做是为了了解它实际上是如何发生的,您可以查看 github 上的源代码以了解函数(展平东西),其中 stuff 是您想要展平的东西。
请注意,对于地图,您必须通过调用 seq 来使用解决方法。
(seq the-map-you-wanna-flatten-eventually)
user=>(展平{:name "Hubert" :age 23})
()
; Workaround for maps
user=> (flatten (seq {:name "Hubert" :age 23}))
(:name "Hubert" :age 23)
你可能想看看 postwalk
这里 http://clojuredocs.org/clojure.walk/postwalk
另见 postwalk-demo
:http://clojuredocs.org/clojure.walk/postwalk-demo
这是一个工作程序:
(ns clj.core
(:use tupelo.core)
(:require [clojure.walk :refer [postwalk]] )
)
(def result (atom []))
(defn go [data]
(postwalk (fn [it]
(spyx it)
(when (keyword? it)
(swap! result append it))
it)
data))
(newline)
(spyx (go {:a 1 :b {:c 3 :d 4}}))
(spyx @result)
结果:
it => :a
it => 1
it => [:a 1]
it => :b
it => :c
it => 3
it => [:c 3]
it => :d
it => 4
it => [:d 4]
it => {:c 3, :d 4}
it => [:b {:c 3, :d 4}]
it => {:a 1, :b {:c 3, :d 4}}
(go {:a 1, :b {:c 3, :d 4}}) => {:a 1, :b {:c 3, :d 4}}
(clojure.core/deref result) => [:a :b :c :d]
使用你的数据,最终输出是:
(clojure.core/deref result) => [:top :top :top :bottom :top :top
:bottom :bottom :bottom :bottom :bottom :bottom :top :top :bottom
:bottom :bottom :top :bottom :bottom]
这是一个简单的递归解决方案:
(def mm {:a 1 :b {:c 3 :d 4}})
(defn accum
[it]
(spy :msg "accum" it)
(when (keyword? it)
(swap! result append it)))
(defn walk [data]
(spy :msg "walk" data)
(cond
(coll? data) (mapv walk data)
:else (accum data)))
(newline)
(reset! result [])
(walk mm)
(spyx @result)
输出:
walk => {:a 1, :b {:c 3, :d 4}}
walk => [:a 1]
walk => :a
accum => :a
walk => 1
accum => 1
walk => [:b {:c 3, :d 4}]
walk => :b
accum => :b
walk => {:c 3, :d 4}
walk => [:c 3]
walk => :c
accum => :c
walk => 3
accum => 3
walk => [:d 4]
walk => :d
accum => :d
walk => 4
accum => 4
(clojure.core/deref result) => [:a :b :c :d]
查看flatten的来源:
(defn flatten
"Takes any nested combination of sequential things (lists, vectors,
etc.) and returns their contents as a single, flat sequence.
(flatten nil) returns an empty sequence."
{:added "1.2"
:static true}
[x]
(filter (complement sequential?)
(rest (tree-seq sequential? seq x))))
您现在只需将 sequential?
更改为 coll?
即可包含地图。此外,如果您只想获取关键字,您还可以添加 every-pred
:
(defn flatten' [x]
(filter (every-pred (complement coll?) keyword?)
(rest (tree-seq coll? seq x))))
如果您的数据嵌套不是很深(比如下百层),您可以简单地使用递归:
(defn my-flatten [x]
(if (coll? x)
(mapcat my-flatten x)
[x]))
回复:
user> (my-flatten sample)
(:top :top :top :bottom :top :top :bottom :bottom :bottom
:bottom :bottom :bottom :top :top :bottom :bottom
:bottom :top :bottom :bottom)
否则我会同意 tree-seq 在这里是非常好的变体:
user> (filter keyword? (tree-seq coll? seq sample))
(:top :top :top :bottom :top :top :bottom :bottom
:bottom :bottom :bottom :bottom :top :top :bottom
:bottom :bottom :top :bottom :bottom)
我正在尝试使用递归遍历 Clojure 中任意嵌套的向量和映射树,以及 return 仅包含关键字的向量,包括顶部。
所以下面的样本数据应该return:
[:top :top :top :top :top :top :top :top :bottom :bottom :bottom :bottom :bottom :bottom :bottom :bottom :bottom :bottom :bottom :bottom]
,
但排名不分先后。
有人可以帮我正确地做到这一点吗?以下是我目前所拥有的。
(def sample [{:top {:top {:top [:bottom {:top {:top [:bottom :bottom :bottom]}} :bottom :bottom :bottom]}}},
{:top {:top [:bottom :bottom :bottom]}},
{:top [:bottom :bottom]}])
(defn make-flat [graph]
(loop [graph graph]
(if (every? keyword? graph) graph
(recur (into graph (flatten (seq (first (filter #(not (keyword? %)) graph)))))))))
(make-flat sample)
我怀疑 clojure.clj = clojure 基础库中已经有一个函数可以执行此操作
果然有
https://clojuredocs.org/clojure.core/flatten
但是,如果您这样做是为了了解它实际上是如何发生的,您可以查看 github 上的源代码以了解函数(展平东西),其中 stuff 是您想要展平的东西。
请注意,对于地图,您必须通过调用 seq 来使用解决方法。
(seq the-map-you-wanna-flatten-eventually)
user=>(展平{:name "Hubert" :age 23}) ()
; Workaround for maps
user=> (flatten (seq {:name "Hubert" :age 23}))
(:name "Hubert" :age 23)
你可能想看看 postwalk
这里 http://clojuredocs.org/clojure.walk/postwalk
另见 postwalk-demo
:http://clojuredocs.org/clojure.walk/postwalk-demo
这是一个工作程序:
(ns clj.core
(:use tupelo.core)
(:require [clojure.walk :refer [postwalk]] )
)
(def result (atom []))
(defn go [data]
(postwalk (fn [it]
(spyx it)
(when (keyword? it)
(swap! result append it))
it)
data))
(newline)
(spyx (go {:a 1 :b {:c 3 :d 4}}))
(spyx @result)
结果:
it => :a
it => 1
it => [:a 1]
it => :b
it => :c
it => 3
it => [:c 3]
it => :d
it => 4
it => [:d 4]
it => {:c 3, :d 4}
it => [:b {:c 3, :d 4}]
it => {:a 1, :b {:c 3, :d 4}}
(go {:a 1, :b {:c 3, :d 4}}) => {:a 1, :b {:c 3, :d 4}}
(clojure.core/deref result) => [:a :b :c :d]
使用你的数据,最终输出是:
(clojure.core/deref result) => [:top :top :top :bottom :top :top :bottom :bottom :bottom :bottom :bottom :bottom :top :top :bottom :bottom :bottom :top :bottom :bottom]
这是一个简单的递归解决方案:
(def mm {:a 1 :b {:c 3 :d 4}})
(defn accum
[it]
(spy :msg "accum" it)
(when (keyword? it)
(swap! result append it)))
(defn walk [data]
(spy :msg "walk" data)
(cond
(coll? data) (mapv walk data)
:else (accum data)))
(newline)
(reset! result [])
(walk mm)
(spyx @result)
输出:
walk => {:a 1, :b {:c 3, :d 4}}
walk => [:a 1]
walk => :a
accum => :a
walk => 1
accum => 1
walk => [:b {:c 3, :d 4}]
walk => :b
accum => :b
walk => {:c 3, :d 4}
walk => [:c 3]
walk => :c
accum => :c
walk => 3
accum => 3
walk => [:d 4]
walk => :d
accum => :d
walk => 4
accum => 4
(clojure.core/deref result) => [:a :b :c :d]
查看flatten的来源:
(defn flatten
"Takes any nested combination of sequential things (lists, vectors,
etc.) and returns their contents as a single, flat sequence.
(flatten nil) returns an empty sequence."
{:added "1.2"
:static true}
[x]
(filter (complement sequential?)
(rest (tree-seq sequential? seq x))))
您现在只需将 sequential?
更改为 coll?
即可包含地图。此外,如果您只想获取关键字,您还可以添加 every-pred
:
(defn flatten' [x]
(filter (every-pred (complement coll?) keyword?)
(rest (tree-seq coll? seq x))))
如果您的数据嵌套不是很深(比如下百层),您可以简单地使用递归:
(defn my-flatten [x]
(if (coll? x)
(mapcat my-flatten x)
[x]))
回复:
user> (my-flatten sample)
(:top :top :top :bottom :top :top :bottom :bottom :bottom
:bottom :bottom :bottom :top :top :bottom :bottom
:bottom :top :bottom :bottom)
否则我会同意 tree-seq 在这里是非常好的变体:
user> (filter keyword? (tree-seq coll? seq sample))
(:top :top :top :bottom :top :top :bottom :bottom
:bottom :bottom :bottom :bottom :top :top :bottom
:bottom :bottom :top :bottom :bottom)