Reputation: 25259
Say I have a function like this:
user=> (def m {10 5, 5 2, 2 1})
#'user/m
user=> (defn hierarchy [x] (when x (cons x (hierarchy (get m x)))))
#'user/hierarchy
user=> (hierarchy 10)
(10 5 2 1)
user=>
And obviously this is fine here because the stack depth will be small. But for this general type of problem, where I'm building a list that I want to return, the recursive call always ends up inside a cons call. How would I convert this to tail recursion, so that I can use recur and not take stack space?
Upvotes: 4
Views: 214
Reputation: 1967
You can solve this elegantly without using recursion:
(defn hierarchy [x]
(take-while identity (iterate m x)))
Upvotes: 3
Reputation: 3752
1st variant
(defn hierarchy* [res x]
(if-not x
res
(recur (conj res x) (get m x))))
(defn hierarchy [x]
(hierarchy* [] x))
2nd
(defn hierarchy [x]
(loop [res []
next-x x]
(if-not next-x
res
(recur (conj res next-x) (get m next-x)))))
Upvotes: 2
Reputation: 4619
add lazy-seq
:
(defn hierarchy [x] (when x (cons x (lazy-seq (hierarchy (get m x))))))
Upvotes: 1
Reputation: 17299
Read up on accumulators.
In Clojure this specific problem could be solved by using lazy-seq
. lazy-seq
defers the computation, so stack overflows are (usually) not an issue.
(defn hierarchy
[x]
(when x
(lazy-seq
(cons x (hierarchy (get m x))))))
Upvotes: 3