I am trying to solve [this][1] problem where you need to create a binary tree that looks like this:
1
/ \
2 3
/ /
4 5
/ /
/ /\
/ / \
6 7 8
\ / \
\ / \
9 10 11
From this input where -1 represents a null node
[2 3] [4 -1] [5 -1] [6 -1] [7 8] [-1 9] [-1 -1] [10 11] [-1 -1] [-1 -1] [-1 -1]
Once you have done that, the problem asks you to swap nodes at a given depth.
So far I have this code that creates the tree:
(ns scratch.core
(require [clojure.string :as str :only (split-lines join split)]))
(defn numberify [str]
(vec (map read-string (str/split str #" "))))
(defrecord TreeNode [val left right])
(defn preprocess-input [n xs]
(let [source (map vector (range 1 n) xs)]
(->> source
(map (fn [[k v]]
{k v}))
(into {}))))
(defn build-tree [val source]
(when-let [[l r] (get source val)]
(TreeNode. val (build-tree l source) (build-tree r source))))
(let [input "11\n2 3\n4 -1\n5 -1\n6 -1\n7 8\n-1 9\n-1 -1\n10 11\n-1 -1\n-1 -1\n-1 -1\n2\n2\n4"
lines (str/split-lines input)
tree-length (read-string (first lines))
tree-lines (map numberify (drop 1 (take (inc tree-length) lines)))
tree-source (preprocess-input tree-length tree-lines)
tree (build-tree 1 tree-source)
swap-depths (map read-string (vec (take-last (Integer/parseInt (get lines (inc tree-length))) lines)))])
I am totally stuck with how to swap the nodes, I have tried this function:
(defn walk-tree [node curr swap-depth]
(when node
(let [left (:left node)
right (:right node)
val (:val node)]
(if (= curr swap-depth)
(TreeNode. val (walk-tree right (inc curr) swap-depth) (walk-tree left (inc curr) swap-depth))
(TreeNode. val (walk-tree left (inc curr) swap-depth) (walk-tree right (inc curr) swap-depth))))))
But I think I should be going BFS rather than DFS because while I can swap a node this way, it gets swapped back when the right node is encountered.
Like I've said in the comment, I'd approach this in these two ways:
Take this as a String manipulation problem. First split it into a seq of lines. Then define a function that takes m and a-seq, and eats the first m elements from the a-seq, and produces a n where n indicates the number of valid nodes in this layer and the remaining seq. Repeat this depth times and you are now at the given depth. Do whatever required. There should be other viable methods as well.
Construct a tree-like structure. But instead of simply keeping the value given at each node, the depth is also attached. Then use clojure.walk/postwalk to traverse the tree, update value when seeing nodes at the given depth.
Related
I am trying to extract two elements of a map with the largest distance. For that, I defined the function for calculating the distance and can obtain the distance between the first element (p1) and other elements of the map. But I need to calculate distances between the second item (p2) and the next ones (p3, p4, p5), the third item (p3) and (p4, p5), the fourth item (p4) and fifth item (p5). Then I need to identify the maximum amount between all distances and return the 2 items with the largest distance and the distance itself. Any help is highly appreciated.
Here is my code:
(defn eclid-dist
[u v]
(Math/sqrt (apply + (map #(* % %) (mapv - u v)))))
(def error
{:p1 [1 2 3]
:p2 [4 5 6]
:p3 [7 8 9]
:p4 [1 2 3]
:p5 [6 5 4]})
(dotimes [i (dec (count error))]
(let [dis (eclid-dist (second (nth (seq error) 0))
(second (nth (seq error) (+ i 1))))
max-error (max dis)]
(println [':dis' dis ':max-error' max-error])))
I tried to save each calculated distance as a vector element separately to prevent overwriting but it was not successful.
You could use the for macro for this. It let's you combine two nested loops to test for all pairs. Then you can use max-key to pick the pair with largest distance:
(defn find-largest-dist-pair [vec-map]
(apply max-key second
(for [[[k0 v0] & r] (iterate rest vec-map)
:while r
[k1 v1] r]
[[k0 k1] (eclid-dist v0 v1)])))
(find-largest-dist-pair error)
;; => [[:p3 :p4] 10.392304845413264]
There is nothing wrong with eclid-dist, you could just use the dedicated Clojure library clojure.math (and ->> thread-last macro for better readability) and rewrite it like this:
(:require [clojure.math :as m])
(defn distance [u v]
(->> (mapv - u v)
(mapv #(m/pow % 2))
(reduce +)
m/sqrt))
Your main problem is, how to create unique pairs of points from your data. You could write a recursive function for this:
(defn unique-pairs [point-seq]
(let [[f & r] point-seq]
(when (seq r)
(concat (map #(vector f %) r)
(unique-pairs r)))))
(def error {:p1 [1 2 3]
:p2 [4 5 6]
:p3 [7 8 9]
:p4 [1 2 3]
:p5 [6 5 4]})
(unique-pairs (vals error))
or use library clojure.math.combinatorics:
Dependency: [org.clojure/math.combinatorics "0.1.6"]
(:require [clojure.math.combinatorics :as combi])
(combi/combinations (vals error) 2)
Note that these functions have slightly different results- it doesn't affect the final result, but if you can, you should use combinations.
Now, you have to compute distance for all these pairs and return the pair with the largest one:
(defn max-distance [point-map]
(->> (combi/combinations (vals point-map) 2)
(map (fn [[u v]] {:u u :v v :distance (distance u v)}))
(apply max-key :distance)))
(max-distance error)
=> {:u [1 2 3], :v [7 8 9], :distance 10.392304845413264}
For a given tree I would like to sum the depth of each node and calculate that recursively (so not with map/flatten/sum).
Is there a way to do that with recur or do I need to use a zipper in this case?
recur is for tail recursion, meaning if you could do it with normal recursion, where the return value is exactly what a single recursive call would return, then you can use it.
Most functions on trees cannot be written in a straightforward way when restricted to using only tail recursion. Normal recursive calls are much more straightforward, and as long as the tree depth is not thousands of levels deep, then normal recursive calls are just fine in Clojure.
The reason you may have found recommendations against using normal recursive calls in Clojure is for cases when the call stack could grow to tens or hundreds of thousands of calls deep, e.g. a recursive call one level deep for each element of a sequence that could be tens or hundreds of thousands of elements long. That would exceed the default maximum call stack depth limits of many run-time systems.
Using normal stack consuming recursion you can accomplish this pretty easily, by doing a depth-first traversal and summing the depth on the way back out.
(defn sum-depths
([tree]
(sum-depths tree 0))
([node depth]
(if-not (vector? node)
depth
(do
(apply
+
(for [child-node (second node)]
(sum-depths child-node (inc depth))))))))
(sum-depths [:root
[:a1
[:b1
[:a2 :b2]]
:c1]])
;; => 6
(sum-depths ["COM"
[["B"
[["C"
[["D"
[["E"
["F"
["J"
[["K"
["L"]]]]]]
"I"]]]]
["G"
["H"]]]]]])
;; => 19
The details depend a little bit on how you model your tree, so the above assumes that a node is either a vector pair where the first element is the value and the second element is a vector of children nodes, or if it is a leaf node then it's not a vector.
So a leaf node is anything that's not a vector.
And a node with children is a vector of form: [value [child1 child2 ...]
And here I assumed you wanted to sum the depth of all leaf nodes. Since I see from your answer, that your example gives 42, I'm now thinking you meant the sum of the depth of every node, not just leaves, if so it only takes one extra line of code to do so:
(defn sum-depths
([tree]
(sum-depths tree 0))
([node depth]
(if-not (vector? node)
depth
(do
(apply
+
depth
(for [child-node (second node)]
(sum-depths child-node (inc depth))))))))
(sum-depths [:root
[:a1
[:b1
[:a2 :b2]]
:c1]])
;; => 7
(sum-depths ["COM"
[["B"
[["C"
[["D"
[["E"
["F"
["J"
[["K"
["L"]]]]]]
"I"]]]]
["G"
["H"]]]]]])
;; => 42
And like your own answer showed, this particular algorithm can be solved without a stack as well, by doing a level order traversal (aka breadth-first traversal) of the tree. Here it is working on my tree data-structure (similar strategy then your own answer otherwise):
(defn sum-depths [tree]
(loop [children (second tree) depth 0 total 0]
(if (empty? children)
total
(let [child-depth (inc depth)
level-total (* (count children) child-depth)]
(recur (into [] (comp (filter vector?) (mapcat second)) children)
child-depth
(+ total level-total))))))
(sum-depths [:root
[:a1
[:b1
[:a2 :b2]]
:c1]])
;; => 7
(sum-depths ["COM"
[["B"
[["C"
[["D"
[["E"
["F"
["J"
[["K"
["L"]]]]]]
"I"]]]]
["G"
["H"]]]]]])
;; => 42
And for completeness, I also want to show how you can do a depth-first recursive traversal using core.async instead of the function call stack in order to be able to traverse trees that would cause a StackOverFlow otherwise, but still using a stack based recursive depth-first traversal instead of an iterative one. As an aside, there exists some non stack consuming O(1) space depth-first traversals as well, using threaded trees (Morris algorithm) or tree transformations, but I won't show those as I'm not super familiar with them and I believe they only work on binary trees.
First, let's construct a degenerate tree of depth 10000 which causes a StackOverFlow when run against our original stack-recursive sum-depths:
(def tree
(loop [i 0 t [:a [:b]]]
(if (< i 10000)
(recur (inc i)
[:a [t]])
t)))
(defn sum-depths
([tree]
(sum-depths tree 0))
([node depth]
(if-not (vector? node)
depth
(do
(apply
+
depth
(for [child-node (second node)]
(sum-depths child-node (inc depth))))))))
(sum-depths tree)
;; => java.lang.StackOverflowError
If it works on your machine, try increasing 10000 to something even bigger.
Now we rewrite it to use core.async instead:
(require '[clojure.core.async :as async])
(defmacro for* [[element-sym coll] & body]
`(loop [acc# [] coll# ~coll]
(if-let [~element-sym (first coll#)]
(recur (conj acc# (do ~#body)) (next coll#))
acc#)))
(def tree
(loop [i 0 t [:a [:b]]]
(if (< i 10000)
(recur (inc i)
[:a [t]])
t)))
(defn sum-depths
([tree]
(async/<!! (sum-depths tree 0)))
([node depth]
(async/go
(if-not (vector? node)
depth
(do
(apply
+
depth
(for* [child-node (second node)]
(async/<!
(sum-depths child-node (inc depth))))))))))
;; => (sum-depths tree)
50015001
It is relatively easy to rewrite a stack-recursive algorithm to use core.async instead of the call stack, and thus make it so it isn't at risk of causing a StackOverFlow in the case of large inputs. Just wrap it in a go block, and wrap the recursive calls in a <! and the whole algorithm in a <!!. The only tricky part is that core.async cannot cross function boundaries, which is why the for* macro is used above. The normal Clojure for macro crosses function boundaries internally, and thus we can't use <! inside it. By rewriting it to not do so, we can use <! inside it.
Now for this particular algorithm, the tail-recursive variant using loop/recur is probably best, but I wanted to show this technique of using core.async for posterity, since it can be useful in other cases where there isn't a trivial tail-recursive implementation.
i would also propose this one, which is kinda straightforward:
it uses more or less the same approach, as tail recursive flatten does:
(defn sum-depth
([data] (sum-depth data 1 0))
([[x & xs :as data] curr res]
(cond (empty? data) res
(coll? x) (recur (concat x [:local/up] xs) (inc curr) res)
(= :local/up x) (recur xs (dec curr) res)
:else (recur xs curr (+ res curr)))))
the trick is that when you encounter the collection at the head of the sequence, you concat it to the rest, adding special indicator that signals the end of branch and level up. It allows you to track the current depth value. Quite simple, and also using one pass.
user> (sum-depth [1 [2 7] [3]])
;;=> 7
user> (sum-depth [1 2 3 [[[[[4]]]]]])
;;=> 9
You can use map/mapcat to walk a tree recursively to produce a lazy-seq (of leaf nodes). If you need depth information, just add it along the way.
(defn leaf-seq
[branch? children root]
(let [walk (fn walk [lvl node]
(if (branch? node)
(->> node
children
(mapcat (partial walk (inc lvl))))
[{:lvl lvl
:leaf node}]))]
(walk 0 root)))
To run:
(->> '((1 2 ((3))) (4))
(leaf-seq seq? identity)
(map :lvl)
(reduce +))
;; => 10
where the depths of each node are:
(->> '((1 2 ((3))) (4))
(leaf-seq seq? identity)
(map :lvl))
;; => (2 2 4 2)
Updates - sum all nodes instead of just leaf nodes
I misread the original requirement and was assuming leaf nodes only. To add the branch node back is easy, we just need to cons it before its child sequence.
(defn node-seq
"Returns all the nodes marked with depth/level"
[branch? children root]
(let [walk (fn walk [lvl node]
(lazy-seq
(cons {:lvl lvl
:node node}
(when (branch? node)
(->> node
children
(mapcat (partial walk (inc lvl))))))))]
(walk 0 root)))
Then we can walk on the hiccup-like tree as before:
(->> ["COM" [["B" [["C" [["D" [["E" [["F"] ["J" [["K" [["L"]]]]]]] ["I"]]]]] ["G" [["H"]]]]]]]
(node-seq #(s/valid? ::branch %) second)
(map :lvl)
(reduce +))
;; => 42
Note: above function uses below helper specs to identify the branch/leaf:
(s/def ::leaf (s/coll-of string? :min-count 1 :max-count 1))
(s/def ::branch (s/cat :tag string? :children (s/coll-of (s/or :leaf ::leaf
:branch ::branch))))
Here's my alternative approach that does use recur:
(defn sum-of-depths
[branches]
(loop [branches branches
cur-depth 0
total-depth 0]
(cond
(empty? branches) total-depth
:else (recur
(mapcat (fn [node] (second node)) branches)
(inc cur-depth)
(+ total-depth (* (count branches) cur-depth))))))
(def tree ["COM" (["B" (["C" (["D" (["E" (["F"] ["J" (["K" (["L"])])])] ["I"])])] ["G" (["H"])])])])
(sum-of-depths [tree]) ; For the first call we have to wrap the tree in a list.
=> 42
You can do this using the Tupelo Forest library. Here is a function to extract information about a tree in Hiccup format. First, think about how we want to use the information for a simple tree with 3 nodes:
(dotest
(hid-count-reset)
(let [td (tree-data [:a
[:b 21]
[:c 39]])]
(is= (grab :paths td) [[1003]
[1003 1001]
[1003 1002]])
(is= (grab :node-hids td) [1003 1001 1002])
(is= (grab :tags td) [:a :b :c])
(is= (grab :depths td) [1 2 2])
(is= (grab :total-depth td) 5) ))
Here is how we calculate the above information:
(ns tst.demo.core
(:use tupelo.forest tupelo.core tupelo.test)
(:require
[schema.core :as s]
[tupelo.schema :as tsk]))
(s/defn tree-data :- tsk/KeyMap
"Returns data about a hiccup tree"
[hiccup :- tsk/Vec]
(with-forest (new-forest)
(let [root-hid (add-tree-hiccup hiccup)
paths (find-paths root-hid [:** :*])
node-hids (mapv xlast paths)
tags (mapv #(grab :tag (hid->node %)) node-hids)
depths (mapv count paths)
total-depth (apply + depths)]
(vals->map paths node-hids tags depths total-depth))))
and an example on a larger Hiccup-format tree:
(dotest
(let [td (tree-data [:a
[:b 21]
[:b 22]
[:b
[:c
[:d
[:e
[:f
[:g 7]
[:h
[:i 9]]]]]]
[:c 32]]
[:c 39]])]
(is= (grab :tags td) [:a :b :b :b :c :d :e :f :g :h :i :c :c])
(is= (grab :depths td) [1 2 2 2 3 4 5 6 7 7 8 3 2])
(is= (grab :total-depth td) 52)))
Don't be afraid of stack size for normal processing. On my computer, the default stack doesn't overflow until you get to a stack depth of over 3900 recursive calls. For a binary tree, just 2^30 is over a billion nodes, and 2^300 is more nodes than the number of protons in the universe (approx).
I am trying to get into Lisps and FP by trying out the 99 problems.
Here is the problem statement (Problem 15)
Replicate the elements of a list a given number of times.
I have come up with the following code which simply returns an empty list []
I am unable to figure out why my code doesn't work and would really appreciate some help.
(defn replicateList "Replicates each element of the list n times" [l n]
(loop [initList l returnList []]
(if (empty? initList)
returnList
(let [[head & rest] initList]
(loop [x 0]
(when (< x n)
(conj returnList head)
(recur (inc x))))
(recur rest returnList)))))
(defn -main
"Main" []
(test/is (=
(replicateList [1 2] 2)
[1 1 2 2])
"Failed basic test")
)
copying my comment to answer:
this line: (conj returnList head) doesn't modify returnlist, rather it just drops the result in your case. You should restructure your program to pass the accumulated list further to the next iteration. But there are better ways to do this in clojure. Like (defn replicate-list [data times] (apply concat (repeat times data)))
If you still need the loop/recur version for educational reasons, i would go with this:
(defn replicate-list [data times]
(loop [[h & t :as input] data times times result []]
(if-not (pos? times)
result
(if (empty? input)
(recur data (dec times) result)
(recur t times (conj result h))))))
user> (replicate-list [1 2 3] 3)
;;=> [1 2 3 1 2 3 1 2 3]
user> (replicate-list [ ] 2)
;;=> []
user> (replicate-list [1 2 3] -1)
;;=> []
update
based on the clarified question, the simplest way to do this is
(defn replicate-list [data times]
(mapcat (partial repeat times) data))
user> (replicate-list [1 2 3] 3)
;;=> (1 1 1 2 2 2 3 3 3)
and the loop/recur variant:
(defn replicate-list [data times]
(loop [[h & t :as data] data n 0 res []]
(cond (empty? data) res
(>= n times) (recur t 0 res)
:else (recur data (inc n) (conj res h)))))
user> (replicate-list [1 2 3] 3)
;;=> [1 1 1 2 2 2 3 3 3]
user> (replicate-list [1 2 3] 0)
;;=> []
user> (replicate-list [] 10)
;;=> []
Here is a version based on the original post, with minimal modifications:
;; Based on the original version posted
(defn replicateList "Replicates each element of the list n times" [l n]
(loop [initList l returnList []]
(if (empty? initList)
returnList
(let [[head & rest] initList]
(recur
rest
(loop [inner-returnList returnList
x 0]
(if (< x n)
(recur (conj inner-returnList head) (inc x))
inner-returnList)))))))
Please keep in mind that Clojure is mainly a functional language, meaning that most functions produce their results as a new return value instead of updating in place. So, as pointed out in the comment, the line (conj returnList head) will not have an effect, because it's return value is ignored.
The above version works, but does not really take advantage of Clojure's sequence processing facilities. So here are two other suggestions for solving your problem:
;; Using lazy seqs and reduce
(defn replicateList2 [l n]
(reduce into [] (map #(take n (repeat %)) l)))
;; Yet another way using transducers
(defn replicateList3 [l n]
(transduce
(comp (map #(take n (repeat %)))
cat
)
conj
[]
l))
One thing is not clear about your question though: From your implementation, it looks like you want to create a new list where each element is repeated n times, e.g.
playground.replicate> (replicateList [1 2 3] 4)
[1 1 1 1 2 2 2 2 3 3 3 3]
But if you would instead like this result
playground.replicate> (replicateList [1 2 3] 4)
[1 2 3 1 2 3 1 2 3 1 2 3]
the answer to your question will be different.
If you want to learn idiomatic Clojure you should try to find a solution without such low level facilities as loop. Rather try to combine higher level functions like take, repeat, repeatedly. If you're feeling adventurous you might throw in laziness as well. Clojure's sequences are lazy, that is they get evaluated only when needed.
One example I came up with would be
(defn repeat-list-items [l n]
(lazy-seq
(when-let [s (seq l)]
(concat (repeat n (first l))
(repeat-list-items (next l) n)))))
Please also note the common naming with kebab-case
This seems to do what you want pretty well and works for an unlimited input (see the call (range) below), too:
experi.core> (def l [:a :b :c])
#'experi.core/
experi.core> (repeat-list-items l 2)
(:a :a :b :b :c :c)
experi.core> (repeat-list-items l 0)
()
experi.core> (repeat-list-items l 1)
(:a :b :c)
experi.core> (take 10 (drop 10000 (repeat-list-items (range) 4)))
(2500 2500 2500 2500 2501 2501 2501 2501 2502 2502)
Consider the following numbers nested by vectors in a tree-structure
(def tree [7 9 [7 5 3 [4 6 9] 9 3] 1 [2 7 9 9]])
My goal is to find the path to the first even number that can be found by traversing the tree: In the upper example this would be 4, the path from the root to this node would be [2 3 0]
(def result [2 3 0])
I got some difficulties writing a function tho archive this. However, the following function finds the first even number, not its path:
(defn find-even [x]
(if (vector? x)
(some #(when-let [subresult (find-even %)]
(when (even? subresult)
subresult)) x)
x))
(find-even tree) ;; 4
What would I have to do in order to receive the path to the result?
EDIT
I figured out a way to do it. Here is a function that - at least - works:
(defn find-even-path [x]
(letfn [(traverse [x]
(if (vector? x)
(some (fn [[i v]] (when-let [subresult (traverse v)]
(when (even? (:value subresult))
(update subresult :path conj i))))
(map-indexed vector x))
{:path '()
:value x}))]
(when-let [result (traverse x)]
(vec (:path result)))))
(find-even-path tree) ;; [2 3 0]
However, I'd still be curious to hear what could be optimized here. It still looks quite complex to me and is not tail recursive yet.
Here is an option. The idea is to keep a "stacktrace" of indices while traversing the list (the r argument). Every time we find an item that satisfies the p predicate, we return that "stacktrace". If none was found, we simply return nil. mapcat concatenates all the non-empty (non-nil) lists into one resulting list:
(defn findt [t p r]
(mapcat (fn[i c]
(if (coll? c)
(findt c p (cons i r))
(when (p c) [(reverse (cons i r))]))) (range) t))
Its still not tail recursive, but it can find all paths (lazily, due to the use of mapcat):
(def tree [7 9 [7 5 3 [4 6 9] 9 3] 1 [2 7 9 9]])
(findt tree even? [])
=> ((2 3 0) (2 3 1) (4 0))
And we can test it with:
(->> (findt tree odd? [])
(map #(get-in tree %))
(every? odd?))
Here's a way to do it tail-recursively:
(defn tailrec-depth-first-path [is? tree]
(loop [[tree i path fk] [tree 0 [] nil]]
(cond
(>= i (count tree))
(if (nil? fk) nil (recur fk))
(vector? (tree i))
(recur [(tree i) 0 (conj path i) [tree (inc i) path fk]])
(is? (tree i))
(conj path i)
:else
(recur [tree (inc i) path fk]))))
(tailrec-depth-first-path even? [7 9 [7 5 3 [4 6 9] 9 3] 1 [2 7 9 9]])
=> [2 3 0]
The function adds one more index to path each time it descends further down the tree. The main trick of note here is the use of a "failure continuation", represented by the variable fk. fk is the next set of arguments to pass to loop to continue the search after a failed search of a subtree, or nil if the search is at the top level. This enables backtracking without violating tail-recursion. In other words, the information that, in a non-tail-recursive version, would be needed to do the work remaining after the recursive call, is accumulated in fk in the tail-recursive version.
A quick test on my 2009 MacBook Air (1.86 GHz Core 2 Duo) suggests that the tail-recursive version is the fastest of the answers posted so far:
(def tree [7 9 [7 5 3 [4 6 9] 9 3] 1 [2 7 9 9]])
(time (dotimes [_ 100000] (find-even-path tree)))
"Elapsed time: 1153.137475 msecs"
(time (dotimes [_ 100000] (first (findt tree even? []))))
"Elapsed time: 1413.502082 msecs"
(time (dotimes [_ 100000] (depth-first-path even? tree)))
"Elapsed time: 690.56115 msecs"
(time (dotimes [_ 100000] (tailrec-depth-first-path even? tree)))
"Elapsed time: 524.332278 msecs"
This isn't tail-recursive, but it's straightforward:
(defn depth-first-path
([is? tree]
(depth-first-path is? tree []))
([is? tree path-so-far]
(cond
(vector? tree)
(some identity (map #(depth-first-path is? %1 (conj path-so-far %2))
tree
(range)))
(is? tree)
path-so-far
:else
nil)))
(depth-first-path even? [7 9 [7 5 3 [4 6 9] 9 3] 1 [2 7 9 9]])
=> [2 3 0]
I called it depth-first-path because there are other reasonable ways to define "first" when searching a tree.
Note: I'm new to Clojure, and I haven't even looked at clojure.walk or Specter. There is probably an easier way.
Introduction
The following function iteratively traverses a tree structure made of nested vectors. It tests each leaf against a predicate. The paths to all leaves which pass that truth-test are returned in a Trie structure. The later describes all found paths in a non-redundant way.
(defn get-trie-of-matches [is? tree]
(loop [[tree i path fk] [tree 0 [] nil]
accum {}]
(cond
(>= i (count tree)) ;; end of level / go up
(if (nil? fk) accum (recur fk accum))
(vector? (tree i)) ;; level down
(recur [(tree i) 0 (conj path i) [tree (inc i) path fk]] accum)
(is? (tree i)) ;; match
(let [new-accum (assoc-in accum (conj path i) {})]
(recur [tree (inc i) path fk] new-accum))
:else ;; next on same level
(recur [tree (inc i) path fk] accum))))
For further explanations see this post.
Example
Consider the following tree
(def tree [7 9 [7 5 3 [4 6 9] 9 3] 1 [2 7 9 9]])
Applied to the function, using even? as a predicate:
(get-trie-of-matches even? tree)
=> {2 {3 {0 {}, 1 {}}}, 4 {0 {}}}
The result describes the three paths to even numbers in tree. Namely 2-3-0, 2-3-1 and 4-0.
Problem
Even though the above function works, there might be better ways to construct the Trie while traversing the tree. At the moment a hash-map is flooded. On each match via assoc-in. The algorithm traverses the tree structure relatively from level to level but attaches each path in a global fashion to accum, which is not necessary. Also this method is only possible since a hashmap is used. It might anyways be better to use a sequential data-structure for the Trie in order to facilitate further iterations over it. This could not be adopted to the above method.
Question
How could a Trie be created from within the above function get-trie-of-matches without relying on hash-map specific 'global' 'write' functions?
I would propose to take a look at clojure's walk api.
It allows you to recursively apply some function to nested collections.
In this case you could use postwalk:
user> (require '[clojure.walk :as w])
user> (w/postwalk-demo [1 3 [4 [6] 7] [[8]]])
Walked: 1
Walked: 3
Walked: 4
Walked: 6
Walked: [6]
Walked: 7
Walked: [4 [6] 7]
Walked: 8
Walked: [8]
Walked: [[8]]
Walked: [1 3 [4 [6] 7] [[8]]]
[1 3 [4 [6] 7] [[8]]]
The key thing is you can replace any item at every step:
user> (w/postwalk #(if (coll? %) (reverse %) (inc %))
[1 3 [4 [6] 7] [[8]]])
(((9)) (8 (7) 5) 4 2)
Here we increment all the numbers, and reverse all the collections, keeping the nested structure.
Now applying to your task:
You could walk through your tree, keeping just even number's indices and not empty collections (e.g. collections containing even numbers, and not empty collections):
;; helper function
(defn empty-coll? [item]
(and (coll? item) (not (seq item))))
(defn make-trie [pred tree]
(w/postwalk
#(if (coll? %)
(keep-indexed (fn [idx item]
(cond (empty-coll? item) nil
(coll? item) (list idx item)
item idx
:else nil))
%)
(pred %))
tree))
in repl:
user> (def tree [7 9 [7 5 3 [4 6 9] 9 3] 1 [2 7 9 9]])
#'user/tree
user> (make-trie even? tree)
((2 ((3 (0 1)))) (4 (0)))
user> (make-trie #(> % 7) tree)
(1 (2 ((3 (2)) 4)) (4 (2 3)))
The structure is similar to your map. In fact you can produce any structure you want with minor changes to the function, for example your map structure:
(defn make-trie-map [pred tree]
(w/postwalk
#(if (coll? %)
(into {}
(keep-indexed (fn [idx item]
(cond (empty-coll? item) nil
(coll? item) {idx item}
item {idx {}}
:else nil))
%))
(pred %))
tree))
user> (make-trie-map even? tree)
{2 {3 {0 {}, 1 {}}}, 4 {0 {}}}