|
13 | 13 | For some example code, see the functions `balance-sheet-example` or
|
14 | 14 | `tree-table-example` in this namespace."
|
15 | 15 | :author "Matthew Downey"} excel-clj.tree
|
16 |
| - (:require |
17 |
| - [clojure.string :as string])) |
| 16 | + (:require [clojure.string :as string])) |
18 | 17 |
|
19 | 18 | ;;; Utilities for vector math
|
20 | 19 |
|
|
65 | 64 | summed))
|
66 | 65 | (second node)))
|
67 | 66 |
|
68 |
| -(defn seq-tree |
69 |
| - "The opposite of core/tree-seq: construct a tree given the root and traversal |
70 |
| - functions." |
71 |
| - ([branch? children root] |
72 |
| - (seq-tree branch? children root identity)) |
73 |
| - ([branch? children root leaf-factory] |
74 |
| - (letfn [(traverse [node] |
75 |
| - (if-not (branch? node) |
76 |
| - (leaf-factory node) |
77 |
| - (fn [] |
78 |
| - [node (mapv #(trampoline traverse %) (children node))])))] |
79 |
| - (trampoline traverse root)))) |
80 |
| - |
81 | 67 | (defn force-map
|
82 | 68 | "Returns the argument if it's a map, otherwise calls `value` on the arg."
|
83 | 69 | [tree-or-map]
|
|
120 | 106 | {'+ `add-trees, '- `subtract-trees}
|
121 | 107 | form))
|
122 | 108 |
|
123 |
| -;;; Modify a tree by mapping over its nodes and reconstructing |
| 109 | +;;; Utilities for constructing & walking trees |
124 | 110 |
|
125 |
| -(defn map-nodes |
| 111 | +(defn walk |
126 | 112 | "Map f across all [label attrs] and [label [child]] nodes."
|
127 |
| - [tree f] |
128 |
| - (letfn [(map' [node] |
129 |
| - (if (leaf? node) |
130 |
| - (f node) |
131 |
| - (fn [] |
132 |
| - (let [recurred (mapv #(trampoline map' %) (children node)) |
133 |
| - children' (or |
134 |
| - (not-empty (vec (filter some? recurred))) |
135 |
| - {})] |
136 |
| - (f [(label node) children'])))))] |
137 |
| - (trampoline map' tree))) |
138 |
| - |
139 |
| -(defn map-leaves |
140 |
| - "Map f across all leaf nodes." |
141 |
| - [tree f] |
142 |
| - (map-nodes tree (fn [node] (cond-> node (leaf? node) f)))) |
143 |
| - |
144 |
| -(defn map-leaf-vals |
145 |
| - "Map f across the value map of all leaves." |
146 |
| - [tree f] |
147 |
| - (map-leaves tree (fn [[label attrs]] [label (f attrs)]))) |
| 113 | + ([f tree] |
| 114 | + (walk f (complement leaf?) children tree)) |
| 115 | + ([f branch? children root] |
| 116 | + (let [walk (fn walk [node] |
| 117 | + (if (branch? node) |
| 118 | + (f node (mapv walk (children node))) |
| 119 | + (f node [])))] |
| 120 | + (walk root)))) |
| 121 | + |
| 122 | +(defn ->tree |
| 123 | + "Construct a tree given the same arguments as `tree-seq`. |
| 124 | +
|
| 125 | + Use in conjunction with some mapping function over the tree to build a tree." |
| 126 | + [branch? children root] |
| 127 | + (walk (fn [node children] [node (vec children)]) branch? children root)) |
| 128 | + |
| 129 | +(comment |
| 130 | + "For example, create a file tree with nodes listing the :size of each file." |
| 131 | + (walk |
| 132 | + (fn [f xs] |
| 133 | + (if-not (seq xs) |
| 134 | + [(.getName f) {:size (.length f)}] |
| 135 | + [(.getName f) xs])) |
| 136 | + #(.isDirectory %) #(.listFiles %) (clojure.java.io/file "."))) |
148 | 137 |
|
149 | 138 | (defn negate-tree
|
150 | 139 | "Negate all of the numbers in a tree."
|
151 | 140 | [tree]
|
152 |
| - (map-leaf-vals tree negate-map)) |
| 141 | + (walk |
| 142 | + (fn [node children] |
| 143 | + (if-not (seq children) |
| 144 | + [(label node) (negate-map (value node))] |
| 145 | + [(label node) children])) |
| 146 | + tree)) |
153 | 147 |
|
154 | 148 | (defn shallow
|
155 | 149 | "'Shallow' the tree one level by getting rid of the root and combining its
|
|
0 commit comments