避免抓住头部

时间:2013-11-20 16:03:13

标签: clojure lazy-evaluation

在欧拉项目的problem 3上我尝试了这个:

(defn range-start-2 [] (map #(+ % 2) (range)))

(defn largest-prime
  "Finds the largest prime factor of n. (n must be >= 2)"
  [n largest-prime unchecked]
  (cond
   (> (first unchecked) n)
   largest-prime

   (= (mod n (first unchecked)) 0)
   (recur n (first unchecked) (filter #(not= (mod % (first unchecked)) 0) unchecked))

   :else
   (recur n largest-prime (filter #(not= (mod % (first unchecked)) 0) unchecked))))

(largest-prime (* 3 7 11 13) 2 (range-start-2))
;=> 13

这适用于小n但是给了我更大的n的堆栈溢出。我假设我正在坚持未经检查的seq的头部,但我无法弄清楚在哪里。使用它作为参数不应该留在头上,是吗?

我读到关闭懒惰的seq可能导致头被关闭,所以我尝试了这个:

(defn largest-prime
  "Finds the largest prime factor of n."
  [n largest-prime unchecked]
  (let [prime (first unchecked)]
    (cond
     (> prime n)
     largest-prime

     (= (mod n prime) 0)
     (recur n prime (filter #(not= (mod % prime) 0) unchecked))

     :else
     (recur n largest-prime (filter #(not= (mod % prime) 0) unchecked)))))

这也不起作用。必须有一种方法来获取lazy-seq的第一个元素的值,而不必坚持它的头部。

(当然有更好的方法来解决欧拉项目问题,但在这里我只对头部控股问题感兴趣)

1 个答案:

答案 0 :(得分:4)

您的问题是filter来电是“堆积”未评估的。 filter未立即应用,但在您从序列中请求元素时应用。当你积累(filter f1 (filter f2 (filter f3 ... (filter f1000 s)...)))时,它会进行一千次深度调用,最终会出现堆栈溢出。

解决方案是不以这种方式使用无限延迟序列。 (如果您的序列是有限的,则可以使用doall避免堆栈溢出。)

请注意,有一些优化可能会使您的问题的其他方法可行。如果您知道3,7和11是3003的因子,那么您实际上不需要检查11到3003之间的所有可能的素数......