2

On problem 3 of the euler project I tried this:

(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

This works for small n but gives me a stack overflow for larger n. I assume that I'm holding on to the head of the unchecked seq, but I can't figure out where. Using it as a parameter shouldn't hold onto the head, should it?

I read that closing over a lazy-seq could cause the head to be held, so I tried this:

(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)))))

This doesn't work either. There has to be a way to get the value of the first element of a lazy-seq without holding on to it's head.

(Of course there are better ways to solve the euler project problem, but here I'm only interested in the head holding issue)

Community
  • 1
  • 1
snowape
  • 1,274
  • 10
  • 23

1 Answers1

4

Your issue is that the filter calls are "stacking up" unevaluated. filter is not immediately applied, but applied when you request an element from the sequence. When you accumulate (filter f1 (filter f2 (filter f3 ... (filter f1000 s)...))), it's a thousand calls deep and eventually you get a stack overflow.

The solution is to not use infinite lazy sequences in this way. (If your sequence were finite, you could avoid the stack overflow with a doall.)

Note that there are optimizations that may make other approaches to your problem viable. If you know that 3, 7, and 11 are factors of 3003, then you don't actually need to check all possible primes between 11 and 3003...

svk
  • 5,854
  • 17
  • 22
  • How come [this](http://clojuredocs.org/clojure_core/clojure.core/lazy-seq#example_1000) example works? Doesn't that also stack up filter calls? – snowape Nov 20 '13 at 16:38
  • 1
    @snowape It doesn't work for large numbers. Try `(take 20 (drop 10000 (sieve (iterate inc 2))))` – svk Nov 20 '13 at 16:53