我是Clojure的新手,我正在努力学习如何使用文件路径在Clojure中创建树。我使用file-seq获取目录下的所有文件,并将它们存储在files
中。输入是一个文件路径,如下所示:
resources/data/2012/05/02/low.xml
resources/data/2012/05/01/low.xml
我可以使用以下方法获取文件夹和文件的所有个人名称:
(for [x files]
(if (.contains (.getPath x) ".json")
(for [y (str/split (.getPath x) #"\\")] y)))
这给了我所有文件夹的列表,但后来我不知道如何将它们组合成1个列表来创建树结构。如果任何答案可以解释他们的代码如何工作,以协助学习。这两个输入的所需输出为:
(resources (data (2012 (05 (02 (low.xml)) (01 (low.xml))))))
答案 0 :(得分:5)
你需要建造树木是这样的:
(defn as-tree [data]
(map (fn [[k vs]] (cons k (as-tree (keep next vs))))
(group-by first data)))
给出一个解析路径列表(或者通常是任何序列),它会创建你的结构:
user> (as-tree [["resources" "data" "2012" "05" "02" "low.xml"]
["resources" "data" "2012" "05" "01" "aaa.xml"]
["resources" "data" "2012" "05" "02" "high.xml"]
["resources" "data" "2012" "05" "01" "xsxs.xml"]
["resources" "data" "2012" "06" "01" "bbb.xml"]
["resources" "data" "2012" "05" "01" "ccc.xml"]
["resources" "data" "2012" "02" "some.xml"]
["resources" "data" "2012" "01" "some2.xml"]
["other-resources" "data" "2015" "10" "some100.xml"]])
;; (("resources"
;; ("data"
;; ("2012"
;; ("05"
;; ("02" ("low.xml")
;; ("high.xml"))
;; ("01" ("aaa.xml")
;; ("xsxs.xml")
;; ("ccc.xml")))
;; ("06"
;; ("01" ("bbb.xml")))
;; ("02" ("some.xml"))
;; ("01" ("some2.xml")))))
;; ("other-resources" ("data" ("2015" ("10" ("some100.xml"))))))
所以在你的情况下,它看起来像这样(项目中.clj
文件的树):
(require '[clojure.string :as cs])
(import 'java.io.File)
(->> (File. ".")
file-seq
(map #(.getPath %))
(filter #(cs/ends-with? % ".clj"))
(map #(cs/split % (re-pattern File/separator)))
as-tree
first)
;;=> ("."
;; ("src"
;; ("playground"
;; ("core.clj")))
;; ("test"
;; ("playground"
;; ("core_test.clj")))
;; ("project.clj"))
答案 1 :(得分:1)
使用给定的文件/目录结构:
/tmp/root
├── file1.txt
├── file2.txt
├── sub
│ ├── file5.txt
│ └── file6.txt
└── sub1
├── emptysub
├── file3.txt
├── file4.txt
└── subsub
└── file99.txt
这是一种用拉链构建(从空树上)的方法,给出了这些路径:
(def paths
(for [x (file-seq (io/file "/tmp/root"))]
(keep not-empty (str/split (.getPath x) #"/"))))
(defn level-loc [loc v] ;; find node with value at same depth as loc
(loop [l loc]
(when l
(let [n (z/node l)]
(cond
(= n v) l
(and (coll? n) (= (first n) v)) (-> l z/down)
:else (recur (-> l z/right)))))))
(defn graft-path [loc path]
(reduce
(fn [[_ path :as loc] p]
(or (level-loc loc p) ;; find existing node
(if (nil? path)
;; appends at top of tree
(-> loc
(z/append-child p)
z/down)
;; inserts at depth
(-> loc
(z/insert-right (list p))
z/right
z/down))))
loc
path))
(defn paths->tree [paths]
(z/root
(reduce
(comp z/seq-zip z/root graft-path)
(z/seq-zip '())
paths)))
产生以下输出:
(paths->tree paths)
=>
("tmp"
("root"
("sub" ("file6.txt") ("file5.txt"))
("sub1" ("emptysub") ("subsub" ("file99.txt")) ("file4.txt") ("file3.txt"))
("file1.txt")
("file2.txt")))
答案 2 :(得分:1)
一种方法是按照此处所述的顺序遍历目录:https://docs.oracle.com/javase/tutorial/essential/io/walk.html
另一种方法是在将路径名拆分为组件字符串后累积结果,然后重复使用plt.axhline(y=-10,linestyle='--',color='red', xmin=0.5)
:
assoc-in
对(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test)
(:require [clojure.string :as str] ))
(defn accum-tree
"Accumulates a file path into a map tree"
[file-elem-tree path-str]
(let [path-elems (str/split path-str #"/")
key-seq (butlast path-elems)
file-name (last path-elems)]
(assoc-in file-elem-tree key-seq file-name)))
的每次调用都是这样的:
accum-tree
单元测试显示最终结果。
path-elems => ["resources" "data" "2012" "05" "02" "low.xml"]
key-seq => ("resources" "data" "2012" "05" "02")
file-name => "low.xml"