У меня есть следующий код Clojure для вычисления числа с определенным «факторизуемым» свойством. (что именно делает код - вторично).
(defn factor-9
([]
(let [digits (take 9 (iterate #(inc %) 1))
nums (map (fn [x] ,(Integer. (apply str x))) (permutations digits))]
(some (fn [x] (and (factor-9 x) x)) nums)))
([n]
(or
(= 1 (count (str n)))
(and (divisible-by-length n) (factor-9 (quot n 10))))))
Теперь я в TCO и понимаю, что Clojure может обеспечить хвостовую рекурсию только в том случае, если это явно указано с помощью ключевого слова recur
. Поэтому я переписал код для этого (заменив factor-9 на recur, являющимся единственным отличием):
(defn factor-9
([]
(let [digits (take 9 (iterate #(inc %) 1))
nums (map (fn [x] ,(Integer. (apply str x))) (permutations digits))]
(some (fn [x] (and (factor-9 x) x)) nums)))
([n]
(or
(= 1 (count (str n)))
(and (divisible-by-length n) (recur (quot n 10))))))
Насколько мне известно, TCO имеет двойную выгоду. Во-первых, он не использует стек так сильно, как вызов без хвостовой рекурсии, и, следовательно, не разрушает его при больших рекурсиях. Во-вторых, я думаю, что, следовательно, это быстрее, поскольку его можно преобразовать в цикл.
Теперь я сделал очень грубый тест и не увидел никакой разницы между двумя реализациями. Я ошибаюсь в своем втором предположении или это как-то связано с работой на JVM (у которой нет автоматической TCO) и recur
с использованием трюка для ее достижения?
Спасибо.