Skip to content

Commit d8431fd

Browse files
committed
Replace unnecesarily specific tree functions with a generic 'walk'
1 parent 4397d42 commit d8431fd

File tree

2 files changed

+34
-54
lines changed

2 files changed

+34
-54
lines changed

src/excel_clj/tree.clj

Lines changed: 33 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@
1313
For some example code, see the functions `balance-sheet-example` or
1414
`tree-table-example` in this namespace."
1515
:author "Matthew Downey"} excel-clj.tree
16-
(:require
17-
[clojure.string :as string]))
16+
(:require [clojure.string :as string]))
1817

1918
;;; Utilities for vector math
2019

@@ -65,19 +64,6 @@
6564
summed))
6665
(second node)))
6766

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-
8167
(defn force-map
8268
"Returns the argument if it's a map, otherwise calls `value` on the arg."
8369
[tree-or-map]
@@ -120,36 +106,44 @@
120106
{'+ `add-trees, '- `subtract-trees}
121107
form))
122108

123-
;;; Modify a tree by mapping over its nodes and reconstructing
109+
;;; Utilities for constructing & walking trees
124110

125-
(defn map-nodes
111+
(defn walk
126112
"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 ".")))
148137

149138
(defn negate-tree
150139
"Negate all of the numbers in a tree."
151140
[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))
153147

154148
(defn shallow
155149
"'Shallow' the tree one level by getting rid of the root and combining its

test/excel_clj/tree_test.clj

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
(ns excel-clj.tree-test
22
(:require [clojure.test :refer :all])
3-
(:require [excel-clj.tree :refer :all]
4-
[clojure.string :as string]))
3+
(:require [excel-clj.tree :refer :all]))
54

65
(def ^:private cash-leaf
76
["Cash" {2018 100M, 2017 85M}])
@@ -32,19 +31,6 @@
3231
(is (= (tree-math (- assets (+ liabilities-equity {2018 1, 2017 2})))
3332
{2018 -1M, 2017 -2M})))))
3433

35-
(deftest map-nodes-test
36-
(let [alt-leaf (assoc cash-leaf 0 "Kash")
37-
doubled-leaf ["Cash" {2018 200M, 2017, 170M}]]
38-
(is (= ["assets" [doubled-leaf (assoc doubled-leaf 0 "Kash")]]
39-
(map-nodes
40-
["Assets" [cash-leaf alt-leaf]]
41-
(fn [node]
42-
(if (leaf? node)
43-
;; Double the value of each leaf
44-
[(first node) (tree-math (+ node node))]
45-
;; Lowercase others
46-
(update node 0 string/lower-case))))))))
47-
4834
(deftest negate-tree-test
4935
(testing "Negates the values in a tree."
5036
(let [[assets liabilities-equity] mock-balance-sheet]

0 commit comments

Comments
 (0)