Reputation: 1099
I'm probably getting ahead of myself, I'm barely into functions on my first day of learning Clojure, but I thought I would be ambitious and make a recursive function to convert floating values to ternary. If I call the function by name instead of using recur
it works great. I understand the problem is that I am just recur
ing the 1-arity version of the function, is there a standard way to handle recursion in multi-arity functions? The book I'm reading doesn't seem to cover it.
(defn float-to-ternary
([x k s]
(def a (int x))
(def r (- x a))
(def carry-string (str s (. Integer toString a 3)))
(cond
(== r 0) carry-string
(> k 20) carry-string
:default (recur (* 3 r) (inc k) carry-string)
)
)
([x]
(def a (int x))
(def r (- x a))
(def carry-string (str (. Integer toString a 3) "."))
(cond
(== r 0) (str (. Integer toString a 3))
:default (recur (* 3 r) 1 carry-string)
)
)
)
Upvotes: 3
Views: 1086
Reputation: 45726
If you want to "recur
into a different arity", just explicitly call the function instead of using recur
:
(defn float-to-ternary
([x k s]
(def a (int x))
(def r (- x a))
(def carry-string (str s (. Integer toString a 3)))
(cond
(== r 0) carry-string
(> k 20) carry-string
:default (recur (* 3 r) (inc k) carry-string)))
([x]
(def a (int x))
(def r (- x a))
(def carry-string (str (. Integer toString a 3) "."))
(cond
(== r 0) (str (. Integer toString a 3))
:default (float-to-ternary (* 3 r) 1 carry-string))))
This is safe. You "spend" one stack frame when you don't use recur
, but the rest of the recursions use recur
, so it's fine.
I have some obligatory suggestions too though:
Don't use def
inside of functions unless you really have a good reason. def
creates global variables that don't go out of scope when the function returns!
Your uses of cond
are unnecessary.
In the first body, you want to return carry-string
for the first two conditions. You could just make that one condition, connecting the two with an or
, which lets you simply use if
.
Since the second use only has two outcomes, if
again makes more sense.
Taking this into consideration, your code would look more like:
(defn float-to-ternary
([x k s]
(let [a (int x)
r (- x a)
carry-string (str s (. Integer toString a 3))]
(if (or (> k 20) (== r 0))
carry-string
(recur (* 3 r) (inc k) carry-string))))
([x]
(let [a (int x)
r (- x a)
carry-string (str (. Integer toString a 3) ".")]
(if (== r 0)
(str (. Integer toString a 3))
(float-to-ternary (* 3 r) 1 carry-string)))))
Upvotes: 9