I'm starting to learn Clojure and have decided that doing some projects on HackerRank is a good way to do that. What I'm finding is that my Clojure solutions are horribly slow. I'm assuming that's because I'm still thinking imperatively or just don't know enough about how Clojure operates. The latest problem I wrote solutions for was Down To Zero II. Here's my Java code
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Solution {
private static final int MAX_NUMBER = 1000000;
private static final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
public static int[] precompute() {
int[] values = new int[MAX_NUMBER];
values[0] = 0;
values[1] = 1;
for (int i = 1; i < MAX_NUMBER; i += 1) {
if ((values[i] == 0) || (values[i] > (values[i - 1] + 1))) {
values[i] = (values[i - 1] + 1);
}
for (int j = 1; j <= i && (i * j) < MAX_NUMBER; j += 1) {
int mult = i * j;
if ((values[mult] == 0) || (values[mult] > (values[i] + 1))) {
values[mult] = values[i] + 1;
}
}
}
return values;
}
public static void main(String[] args) throws Exception {
int numQueries = Integer.parseInt(reader.readLine());
int[] values = Solution.precompute();
for (int loop = 0; loop < numQueries; loop += 1) {
int query = Integer.parseInt(reader.readLine());
System.out.println(values[query]);
}
}
}
My Clojure implementation is
(def MAX-NUMBER 1000000)
(defn set-i [out i]
(cond
(= 0 i) (assoc out i 0)
(= 1 i) (assoc out i 1)
(or (= 0 (out i))
(> (out i) (inc (out (dec i)))))
(assoc out i (inc (out (dec i))))
:else out))
(defn set-j [out i j]
(let [mult (* i j)]
(if (or (= 0 (out mult)) (> (out mult) (inc (out i))))
(assoc out mult (inc (out i)))
out)))
;--------------------------------------------------
; Precompute the values for all possible inputs
;--------------------------------------------------
(defn precompute []
(loop [i 0 out (vec (repeat MAX-NUMBER 0))]
(if (< i MAX-NUMBER)
(recur (inc i) (loop [j 1 new-out (set-i out i)]
(if (and (<= j i) (< (* i j) MAX-NUMBER))
(recur (inc j) (set-j new-out i j))
new-out)))
out)))
;--------------------------------------------------
; Read the number of queries
;--------------------------------------------------
(def num-queries (Integer/parseInt (read-line)))
;--------------------------------------------------
; Precompute the solutions
;--------------------------------------------------
(def values (precompute))
;--------------------------------------------------
; Read and process each query
;--------------------------------------------------
(loop [iter 0]
(if (< iter num-queries)
(do
(println (values (Integer/parseInt (read-line))))
(recur (inc iter)))))
The Java code runs in about 1/10 of a second on my machine, while the Clojure code takes close to 2 seconds. Since it's the same machine, with the same JVM, it means I'm doing something wrong in Clojure.
How do people go about trying to translate this type of code? What are the gotchas that are causing it to be so much slower?