I'm writing a simple function: given a number, it will return a sequence or a collection of the digits in the number (in the correct order). i.e (get-digits-fn 1234567) => (1 2 3 4 5 6 7) / [1 2 3 4 5 6 7]
Below are five attempts at the same function:
(defn get-digits-1 [num]
(->> [num '()]
(iterate (fn [[num digits]]
(when (> num 0)
[(quot num 10) (conj digits (rem num 10))])))
(take-while some?)
(last)
(second)))
(defn get-digits-2 [num]
(when (> num 0)
(lazy-seq (concat (get-digits-2 (quot num 10)) '((rem num 10))))))
;; Suggested by Carcigenate
(defn get-digits-3 [num]
(->> (str)
(map str)
(map int)
(into '())))
(defn get-digits-4 [num]
(loop [n num
res '()]
(if (= n 0)
res
(recur (quot n 10) (conj res (rem n 10))))))
(defn get-digits-5 [num]
(->>
(iterate (fn [[n digits]]
[(quot n 10) (conj digits (rem n 10))])
[num '()])
(drop-while #(not= 0 (first %)))
(first)
(second)))
A helper function for testing performance:
(defn quick-bench-get-digits [fn range]
(quick-bench (->> range
(map fn)
(map (partial apply vector))
(into []))))
The perf results (output truncated to only show execution time mean):
eul> (quick-bench-get-digits get-digits-1 (range 1 100000))
Execution time mean : 129.516521 ms
eul> (quick-bench-get-digits get-digits-2 (range 1 100000))
Execution time mean : 128.637055 ms
eul> (quick-bench-get-digits get-digits-3 (range 1 100000))
Execution time mean : 24.267716 ms
eul> (quick-bench-get-digits get-digits-4 (range 1 100000))
Execution time mean : 25.083393 ms
eul> (quick-bench-get-digits get-digits-5 (range 1 100000))
Execution time mean : 145.430443 ms
It looks like get-digits-3 is the fastest while get-digits-4 is closely behind. (As the numbers increase, get-digits-3 outperforms get-digits-4. i.e try (range 1000000 2000000))
- Any way to increase performance more without leaving Clojure land?
- If mutability and Java inter-op is allowed, is there a way to increase performance?
p.s. functions 1 and 5 are almost identical. This was incremental exploration.