如何在Clojure中递归展平任意嵌套的矢量和地图?

时间:2016-05-16 00:04:31

标签: recursion clojure tree nested flatten

我正在尝试使用递归来在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)

4 个答案:

答案 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-demohttp://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]