DEV Community

Cover image for Clojure Is Awesome!!! [PART 7]
André Borba
André Borba

Posted on • Edited on

1

Clojure Is Awesome!!! [PART 7]

(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 []}
)
Enter fullscreen mode Exit fullscreen mode

AWS GenAI LIVE image

Real challenges. Real solutions. Real talk.

From technical discussions to philosophical debates, AWS and AWS Partners examine the impact and evolution of gen AI.

Learn more

Top comments (0)

Tiger Data image

🐯 🚀 Timescale is now TigerData: Building the Modern PostgreSQL for the Analytical and Agentic Era

We’ve quietly evolved from a time-series database into the modern PostgreSQL for today’s and tomorrow’s computing, built for performance, scale, and the agentic future.

So we’re changing our name: from Timescale to TigerData. Not to change who we are, but to reflect who we’ve become. TigerData is bold, fast, and built to power the next era of software.

Read more

👋 Kindness is contagious

Dive into this thoughtful piece, beloved in the supportive DEV Community. Coders of every background are invited to share and elevate our collective know-how.

A sincere "thank you" can brighten someone's day—leave your appreciation below!

On DEV, sharing knowledge smooths our journey and tightens our community bonds. Enjoyed this? A quick thank you to the author is hugely appreciated.

Okay