(defstruct node :elt :l :r) (defn bst-insert [bst x comp] (if (nil? bst) (struct node x) (let [elt (:elt bst)] (if (= x elt) bst (if (comp x elt) (struct node elt (bst-insert (:l bst) x comp) (:r bst)) (struct node elt (:l bst) (bst-insert (:r bst) x comp)))))))
The function is non-destructive. Creating a BST can be done like this:
(def nums (reduce (fn [acc x] (bst-insert acc x <)) nil [5 8 4 2 1 9 6 7 3]))Obviously the function does not use a tail recursion. The value of the recursive call to
(defn bst-insert-tc [bst x comp] (loop [bst bst acc '()] (if (nil? bst) (loop [tree (struct node x) stack acc] (let [[lr elt branch] (first stack)] (if (empty? stack) tree (if (= lr :l) (recur (struct node elt branch tree) (rest stack)) (recur (struct node elt tree branch) (rest stack)))))) (let [elt (:elt bst)] (if (= x elt) bst (if (comp x elt) (recur (:l bst) (cons [:r elt (:r bst)] acc)) (recur (:r bst) (cons [:l elt (:l bst)] acc))))))))This approach works but is a bit naive and the second loop can be simplified by using reduce:
(defn bst-insert-tc2 [bst x comp] (loop [bst bst acc '()] (if (nil? bst) (reduce (fn [tree [lr elt branch]] (if (= lr :l) (struct node elt branch tree) (struct node elt tree branch))) (struct node x) acc) (let [elt (:elt bst)] (if (= x elt) bst (if (comp x elt) (recur (:l bst) (cons [:r elt (:r bst)] acc)) (recur (:r bst) (cons [:l elt (:l bst)] acc))))))))Once I had the first tail recursive version, I asked my friend Yogi how he would transform the
(struct node elt l (:r bst))The problem is, when we are doing this operation, we do not know the value of
(defn bst-insert-tc3 [bst x cmp] (loop [bst bst f identity] (if (nil? bst) (f (struct node x)) (let [elt (:elt bst)] (if (= x elt) bst (if (cmp x elt) (recur (:l bst) (comp f (fn [l] (struct node elt l (:r bst))))) (recur (:r bst) (comp f (fn [r] (struct node elt (:l bst) r))))))))))The code shows how functional programming can be both elegant and efficient.
The full code for the BST is available