Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Recursion clojure中的深度优先树遍历累积_Recursion_Clojure_Tree_Clojurescript - Fatal编程技术网

Recursion clojure中的深度优先树遍历累积

Recursion clojure中的深度优先树遍历累积,recursion,clojure,tree,clojurescript,Recursion,Clojure,Tree,Clojurescript,我想要一个树状结构,像这样: {"foo" {"bar" "1" "baz" "2"}} ["foo/bar/1", "foo/baz/2"] 递归地遍历,同时记住从根开始的路径,以产生如下结果: {"foo" {"bar" "1" "baz" "2"}} ["foo/bar/1", "foo/baz/2"] 关于如何在没有拉链或clojure.walk的情况下做到这一点,有什么建议吗?我使用accumulator做了一些非常快速的事情,但这不是深度优先 (defn paths [sep

我想要一个树状结构,像这样:

{"foo" {"bar" "1" "baz" "2"}}
["foo/bar/1", "foo/baz/2"]
递归地遍历,同时记住从根开始的路径,以产生如下结果:

{"foo" {"bar" "1" "baz" "2"}}
["foo/bar/1", "foo/baz/2"]

关于如何在没有拉链或clojure.walk的情况下做到这一点,有什么建议吗?

我使用accumulator做了一些非常快速的事情,但这不是深度优先

(defn paths [separator tree]
  (let [finished? (fn [[_ v]] ((complement map?) v))]
    (loop [finished-paths nil
           path-trees (seq tree)]
      (let [new-paths (mapcat
                        (fn [[path children]]
                          (map
                            (fn [[k v]]
                              (vector (str path separator k) v))
                            children))
                        path-trees)
            finished (->> (filter finished? new-paths)
                              (map
                                (fn [[k v]]
                                  (str k separator v)))
                              (concat finished-paths))
            remaining-paths (remove finished? new-paths)]
        (if (seq remaining-paths)
          (recur finished remaining-paths)
          finished)))))
在repl中

(clojure-scratch.core/paths "/" {"foo" {"bar" {"bosh" "1" "bash" "3"} "baz" "2"}})
=> ("foo/baz/2" "foo/bar/bash/3" "foo/bar/bosh/1")

以下使用递归深度优先遍历:

(defn combine [k coll]
  (mapv #(str k "/" %) coll))

(defn f-map [m]
  (into []
        (flatten
         (mapv (fn [[k v]]
                 (if (map? v)
                   (combine k (f-map v))
                   (str k "/" v)))
               m))))

(f-map {"foo" {"bar" "1" "baz" "2"}})
=> ["foo/bar/1" "foo/baz/2"]
以下是我的看法:

(defn traverse [t]
  (letfn [(traverse- [path t]
            (when (seq t)
              (let [[x & xs] (seq t)
                    [k v] x]
                (lazy-cat
                  (if (map? v)
                    (traverse- (conj path k) v)
                    [[(conj path k) v]]) 
                  (traverse- path xs)))))]
    (traverse- [] t)))

(traverse {"foo" {"bar" "1" "baz" "2"}})
;=> [[["foo" "bar"] "1"] [["foo" "baz"] "2"]]
遍历返回路径叶对的惰性序列。然后,您可以对每个路径叶应用任何转换,例如“/path/to/leaf”完整路径表单:

(def ->full-path #(->> (apply conj %) (clojure.string/join "/")))

(->> (traverse {"foo" {"bar" "1" "baz" "2"}})
     (map ->full-path))
;=> ("foo/bar/1" "foo/baz/2")

(->> (traverse {"foo" {"bar" {"buzz" 4 "fizz" "fuzz"} "baz" "2"} "faa" "fee"})
     (map ->full-path))
;=> ("foo/bar/buzz/4" "foo/bar/fizz/fuzz" "foo/baz/2" "faa/fee")
同样,我们将枚举路径与将路径表示为字符串分开

枚举

功能

(defn paths [x]
  (if (map? x)
    (mapcat (fn [[k v]] (map #(cons k %) (paths v))) x)
    [[x]]))
#(clojure.string/join \/ %)
。。。返回嵌套映射的路径序列序列。比如说,

(paths {"foo" {"bar" "1", "baz" "2"}})
;(("foo" "bar" "1") ("foo" "baz" "2"))
(#(clojure.string/join \/ %) (list "foo" "bar" "1"))
;"foo/bar/1"
(traverse  {"foo" {"bar" "1", "baz" "2"}})
;("foo/bar/1" "foo/baz/2")
演示文稿

功能

(defn paths [x]
  (if (map? x)
    (mapcat (fn [[k v]] (map #(cons k %) (paths v))) x)
    [[x]]))
#(clojure.string/join \/ %)
。。。用“/”s将字符串连接在一起。比如说,

(paths {"foo" {"bar" "1", "baz" "2"}})
;(("foo" "bar" "1") ("foo" "baz" "2"))
(#(clojure.string/join \/ %) (list "foo" "bar" "1"))
;"foo/bar/1"
(traverse  {"foo" {"bar" "1", "baz" "2"}})
;("foo/bar/1" "foo/baz/2")
编写这些函数以获得所需的函数:

(def traverse (comp (partial map #(clojure.string/join \/ %)) paths))
。。。或者干脆

(defn traverse [x]
  (->> x
      paths
      (map #(clojure.string/join \/ %))))
比如说,

(paths {"foo" {"bar" "1", "baz" "2"}})
;(("foo" "bar" "1") ("foo" "baz" "2"))
(#(clojure.string/join \/ %) (list "foo" "bar" "1"))
;"foo/bar/1"
(traverse  {"foo" {"bar" "1", "baz" "2"}})
;("foo/bar/1" "foo/baz/2")

  • 您可以将这些功能作为一个单一功能进行组合:更清晰、更有用 我想把它们分开
  • 枚举不是惰性的,因此它将耗尽 在嵌套足够深的贴图上堆叠空间

这是我尝试使用
tree-seq
clojure核心函数

(def tree {"foo" {"bar" "1" "baz" "2"}})

(defn paths [t]
  (let [cf (fn [[k v]] 
               (if (map? v)
                 (->> v
                      (map (fn [[kv vv]] 
                             [(str k "/" kv) vv]))
                      (into {}))
                 (str k "/" v)))]
  (->> t 
       (tree-seq map? #(map cf %))
       (remove map?)
       vec)))

(paths tree) ; => ["foo/bar/1" "foo/baz/2"]

映射键用于累积路径。

我正在尝试在clojure中进行递归深度优先遍历,我很好奇拉链是否是完成这类任务的唯一方法?因为这不是使用recur,那么这个版本可能会遇到太深的树的堆栈溢出,对吗?@DaveParoulek是的,它可以处理的结构深度取决于VM堆栈大小。