Background
I am doing an exercise in clojure to make a function that can determine if a number is an Armstrong Number or not.
The solution I came up with us a bit more complicated than it would be if there was not a requirement to take 17 digit numbers which start to cause calculation errors with Math/pow etc...
Tests
(ns armstrong-numbers-test
(:require [clojure.test :refer [deftest is testing]]
[armstrong-numbers :refer [armstrong?]]))
(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)))))
(deftest armstrong-number-21897142587612075
(testing "Seventeen digit number that is an Armstrong number"
(is (armstrong? 21897142587612075))))
My Solution
(ns armstrong-numbers)
(defn less-than-one? [input] (< input 1))
(defn one-tenth [input] (/ (- input (mod input 10)) 10))
(defn for-every-digit [input accumulator accumulator-function]
(if (less-than-one? input)
accumulator
(for-every-digit (one-tenth input) (accumulator-function input accumulator) accumulator-function)))
(defn pow-by-reduction [x n] (reduce * (repeat n x)))
(defn armstrong? [num]
(let [num-digits (for-every-digit num 0 (fn [input accumulator] (+ accumulator 1)))]
(== num (for-every-digit num 0 (fn [input accumulator] (+ accumulator (pow-by-reduction (mod input 10) num-digits)))))))
Update
I worked on it some more, and got it down to this which I am pretty happy with...
(ns armstrong-numbers)
(defn one-tenth [input] (/ (- input (mod input 10)) 10))
(defn pow-by-reduction [x n] (reduce * (repeat n x)))
(defn armstrong? [num]
(== num (let [seq (map (fn [x] (mod x 10)) (take-while (fn [x] (>= x 1)) (iterate one-tenth num)))]
(apply + (map (fn [x] (pow-by-reduction x (count seq))) seq)))))