Exercism - Armstrong Numbers
An Armstrong number is a number that is the sum of its own digits each raised to the power of the number of digits.
For example:
- 9 is an Armstrong number, because 9 = 91 = 9
- 10 is not an Armstrong number, because 10 != 12 + 02 = 1
- 153 is an Armstrong number, because: 153 = 13 + 53 + 33 = 1 + 125 + 27 = 153
- 154 is not an Armstrong number, because: 154 != 13 + 53 + 43 = 1 + 125 + 64 = 190
Write some code to determine whether a number is an Armstrong number.
(ns armstrong-numbers
(:require [cljs.test :refer-macros [deftest testing is run-tests]]))
(defn armstrong? [n]
)
(deftest armstrong-number-5
(testing "Single digit numbers are Armstrong numbers"
(is (armstrong? 5))))
(deftest not-armstrong-number-10
(testing "There are no 2 digit Armstrong numbers"
(is (not (armstrong? 10)))))
(deftest armstrong-number-153
(testing "Three digit number that is an Armstrong number"
(is (armstrong? 153))))
(deftest not-armstrong-number-100
(testing "Three digit number that is not an Armstrong number"
(is (not (armstrong? 100)))))
(deftest armstrong-number-9474
(testing "Four digit number that is an Armstrong number"
(is (armstrong? 9474))))
(deftest not-armstrong-number-9475
(testing "Four digit number that is not an Armstrong number"
(is (not (armstrong? 9476)))))
(deftest armstrong-number-9926315
(testing "Seven digit number that is an Armstrong number"
(is (armstrong? 9926315))))
(deftest not-armstrong-number-9926314
(testing "Seven digit number that is not an Armstrong number"
(is (not (armstrong? 9926314)))))
(run-tests)
This was my first iteration.
Note: The code on this page is modified slightly for Clojurescript. js/parseInt
is JavaScript interop and will not work in Clojure. Instead, use Character/digit
or Integer/parseInt
. Or better yet - see below for a way to do it with math.
(defn digits [n]
(map #(js/parseInt (str %)) (seq (str n))))
(defn exp [x n]
(reduce * (repeat n x)))
(defn numdigits [n]
(count (digits n)))
(defn multiplier [n]
(map #(exp % (numdigits n)) (digits n)))
(defn adder [n]
(reduce + (multiplier n)))
(defn armstrong? [n]
(= (adder n) n))
(run-tests)
I tried to tackle the problem in multiple stages, defining functions that each get us one step closer to the solution. It worked, but my mentor encouraged me to try a more idiomatic approach. So I basically took the functions and squashed them together:
(defn digits [n]
(map #(js/parseInt (str %))
(seq (str n))))
(defn armstrong [n]
(reduce + (map #(Math/pow % (count (digits n)))
(digits n))))
(defn armstrong? [n]
(== (armstrong n) n))
(run-tests)
Also by using Java's Math/pow
I got to eliminate the exponent function. I decided to take this and run with it, however we still could do better:
(defn armstrong? [n]
(let [digits (->> n str (map #(js/parseInt % 10)))]
(->> digits
(map #(Math/pow % (count digits)))
(apply +)
(== n))))
(run-tests)
The difference is mostly stylistic, but readability ought to be a strong priority. By using the threading macros, it is much easier to discern the order of operations.
Here's how to get the digits of a number without turning it into a string or using interop:
(defn digits [n]
(if (>= n 10)
(conj (digits (quot n 10))
(rem n 10))
[n]))
(digits 1234)