我正在尝试使用递归来在Clojure中移动任意嵌套矢量和地图的树,并返回仅包含顶部的关键字的矢量。
因此下面的示例数据应该返回:
[: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)
答案 0 :(得分:3)
看看压扁的来源:
(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))))
答案 1 :(得分:3)
如果你的数据没有深度嵌套(比如下几百个级别),你可以简单地使用递归:
(defn my-flatten [x]
(if (coll? x)
(mapcat my-flatten x)
[x]))
在repl中:
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)
答案 2 :(得分:1)
我怀疑已经有一个函数在clojure.clj = clojure基础库中执行此操作
当然,有
https://clojuredocs.org/clojure.core/flatten
但是,如果你这样做是为了了解它实际发生的原因,你可以在github上查看源代码中的函数(展平东西),其中东西是你想要展平的东西。
请注意,对于地图,您必须通过调用seq。
来使用变通方法whitelisted.Contains(sentRequest.Sender.Username.ToLower())
用户=> (flatten {:name“Hubert”:23岁}) ()
(seq the-map-you-wanna-flatten-eventually)
答案 3 :(得分:0)
您可能希望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]