I wrote a simple program to sum the digits of a number in Clojure for learning purpose. Not very complex (I hope) so not adding any explanation. I am a beginner in Clojure so please don't mind pointing out even the most obvious mistakes.
NOTE: It recursively calculates the sum of digits in the result until a single digit result is obtained. For example, given "4312", 1 is returned (4312 -> 10 -> 1).
(defn sum-once [x] (reduce + (map #(Integer/parseInt (str %)) (seq (char-array x)))))(defn sum-digits [x] (let [y (sum-once x)] (if (< y 10) y (sum-digits (str y)))))- \$\begingroup\$@ferada What is the language tag that you used?\$\endgroup\$Aseem Bansal– Aseem Bansal2015-11-12 12:59:57 +00:00CommentedNov 12, 2015 at 12:59
- \$\begingroup\$should it work - since
(sum-digits "4312")gives 1\$\endgroup\$birdspider– birdspider2015-11-12 13:08:23 +00:00CommentedNov 12, 2015 at 13:08 - \$\begingroup\$@birdspider Yeah it is supposed to work like that. Recursively till a single digit output is given. Added that in the question. Sorry for the confusion.\$\endgroup\$Aseem Bansal– Aseem Bansal2015-11-12 13:09:26 +00:00CommentedNov 12, 2015 at 13:09
- \$\begingroup\$@AseemBansal I marked it as Clojure, so if you click on
edityou'll seelang-cljas the language tag.\$\endgroup\$ferada– ferada2015-11-12 13:26:28 +00:00CommentedNov 12, 2015 at 13:26
3 Answers3
Retaining your names, I'd rewrite the functions as follows:
(defn sum-once [x] (reduce + (map #(- (int %) (int \0)) (str x))))(defn sum-digits [x] (let [y (sum-once x)] (if (< y 10) y (recur y))))The only substantive change toCasey's answer is to replace the recursive call ofsum-digits withrecur. This recognises tail recursion and implements it by agoto: faster and not subject to stack overflow.
Edit
We can probably (untested) make things faster by calculating the digit sum inline:
(defn sum-digits [x] (let [y (loop [x (long x), ans 0] (if (zero? x) ans (recur (quot x 10) (+ ans (mod x 10)))))] (if (< y 10) y (recur y))))This avoids layers of sequence functions and the boxing and unboxing that goes with calling global functions.
- There is no conversion to and from characters.
- We get the digits right to left.
- The
(long x)conversion is a hint enabling the compiler to inferthat everything is along.
By the way, you can get the same result from the remainder of dividing the original number by 9, except that 0 translates to 9, unless the original number was zero:
(defn sum-digits [n] (if (zero? n) 0 (let [r (mod n 9)] (if (zero? r) 9 r))))- \$\begingroup\$boxing and unboxing that goes with calling global. Could you elaborate that a bit?\$\endgroup\$Aseem Bansal– Aseem Bansal2015-11-14 14:22:54 +00:00CommentedNov 14, 2015 at 14:22
- \$\begingroup\$@AseemBansal Arguments are passed to and the result returned from Clojure functions as Java
Objects. This is evident in theinvokemethods ofclojure.core.IFn. So numbers such aslongs have to be passed asObjects such asLongs.\$\endgroup\$Thumbnail– Thumbnail2015-11-14 16:10:37 +00:00CommentedNov 14, 2015 at 16:10
I was confused by your code until reading the comments. You want to change the name ofsum-once tosum-digits ordigit-sum (pick this one!) or anything like it, since that's what it really does.
Your currentsum-digits should becomedigital-root (that'sthe term for what you're trying to achieve).
The digital root (also repeated digital sum) of a non-negative integer is the (single digit) value obtained by an iterative process of summing digits, on each iteration using the result from the previous iteration to compute a digit sum. The process continues until a single-digit number is reached.
Stylistically this is pretty good. I have two stylistic suggestions, plus a bit of code-golf:
- Stylistically, I would prefer for
sum-onceto take a number and callstrthere. Also, there are a few standards around "subsidiary" functions - I prefer a "-" prefix. So it would besum-digitsand-sum-digits. You don't need to convert a string to a
char-array- it is already a sequence.user=> (seq "4312")(\4 \3 \1 \2)This is total code-golf, but you can just subtract individual characters from the ASCII value of 0, which happens to be 48:
(defn -sum-digits [val] (reduce + (map #(- (int %) 48) (str val))))
- \$\begingroup\$In (3), replace magic number
48with self-explanatory(int \0)?\$\endgroup\$Thumbnail– Thumbnail2015-11-13 14:55:49 +00:00CommentedNov 13, 2015 at 14:55
You mustlog in to answer this question.
Explore related questions
See similar questions with these tags.

