(ns tree
(:require [clojure.spec.alpha :as s]))
(s/def ::value any?)
(s/def ::children (s/coll-of ::tree :kind vector?))
(s/def ::tree (s/keys :req-un [::value]
:opt-un [::children]))
(defprotocol TreeOperations
"Protocol defining fundamental tree operations"
(add-child [this child-value] "Adds a new child node with the specified value")
(remove-child [this index] "Removes the child at the specified index")
(update-node-value [this new-value] "Updates the value of the current node")
(get-child-at [this index] "Gets the child node at the specified index")
(get-tree-depth [this] "Calculates the depth of the tree")
(get-tree-size [this] "Calculates the total number of nodes in the tree"))
(defrecord TreeNode [value children]
TreeOperations
(add-child [this child-value]
(let [new-child (->TreeNode child-value [])]
(if (s/valid? ::tree new-child)
(update this :children (fnil conj []) new-child)
(throw (ex-info "Invalid child node"
{:value child-value
:spec-error (s/explain-str ::tree new-child)})))))
(remove-child [this index]
(if (and (>= index 0) (< index (count (:children this))))
(update this :children #(vec (concat (subvec % 0 index)
(subvec % (inc index)))))
this))
(update-node-value [this new-value]
(let [updated (assoc this :value new-value)]
(if (s/valid? ::tree updated)
updated
(throw (ex-info "Invalid node value"
{:value new-value
:spec-error (s/explain-str ::tree updated)})))))
(get-child-at [this index]
(get-in this [:children index]))
(get-tree-depth [this]
(if (empty? (:children this))
1
(inc (apply max (map get-tree-depth (:children this))))))
(get-tree-size [this]
(inc (reduce + 0 (map get-tree-size (:children this))))))
(defn create-tree
"Creates a new tree with the specified value"
[value]
(let [tree (->TreeNode value [])]
(if (s/valid? ::tree tree)
tree
(throw (ex-info "Invalid tree structure"
{:value value
:spec-error (s/explain-str ::tree tree)})))))
(defn pre-order
"Performs a pre-order traversal of the tree"
[tree]
(when (s/valid? ::tree tree)
(cons (:value tree)
(mapcat pre-order (:children tree)))))
(defn post-order
"Performs a post-order traversal of the tree"
[tree]
(when (s/valid? ::tree tree)
(concat (mapcat post-order (:children tree))
[(:value tree)])))
(defn breadth-first
"Performs a breadth-first traversal of the tree"
[tree]
(when (s/valid? ::tree tree)
(loop [queue [tree]
result []]
(if (empty? queue)
result
(let [current (first queue)]
(recur (concat (rest queue) (:children current))
(conj result (:value current))))))))
(defn find-node
"Finds a node in the tree that satisfies the predicate"
[tree pred]
(when (s/valid? ::tree tree)
(cond
(pred tree) tree
(empty? (:children tree)) nil
:else (some #(find-node % pred) (:children tree)))))
(defn map-tree
"Applies function f to each node value in the tree"
[f tree]
(when (s/valid? ::tree tree)
(->TreeNode (f (:value tree))
(mapv #(map-tree f %) (:children tree)))))
(comment
(def sample-tree
(-> (create-tree 1)
(add-child 2)
(add-child 3)
(add-child 4)))
(def nested-tree
(-> sample-tree
(add-child 5)
(add-child 6)))
(pre-order nested-tree)
;; => (1 2 3 4 5 6)
(post-order nested-tree)
;; => (2 3 4 5 6 1)
(breadth-first nested-tree)
;; => [1 2 3 4 5 6]
(get-tree-depth nested-tree) ;; => 2
(get-tree-size nested-tree) ;; => 6
(map-tree #(* 2 %) nested-tree)
;; => TreeNode with doubled values
(find-node nested-tree #(= 3 (:value %)))
;; => TreeNode{:value 3, :children []}
)
![Cover image for Clojure Is Awesome!!! [PART 7]](https://media2.dev.to/dynamic/image/width=1000,height=420,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft7eo4be5gv4k2r320vky.jpg)
See why 4M developers consider Sentry, “not bad.”
Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.
For further actions, you may consider blocking this person and/or reporting abuse
Top comments (0)