Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is this Haskell code snippet not infinitely recursive?

To help me learn Haskell, I am working through the problems on Project Euler. After solving each problem, I check my solution against the Haskell wiki in an attempt to learn better coding practices. Here is the solution to problem 3:

primes = 2 : filter ((==1) . length . primeFactors) [3,5..]

primeFactors n = factor n primes
  where
    factor n (p:ps) 
        | p*p > n        = [n]
        | n `mod` p == 0 = p : factor (n `div` p) (p:ps)
        | otherwise      = factor n ps

problem_3 = last (primeFactors 317584931803)

My naive reading of this is that primes is defined in terms of primeFactors, which is defined in terms of primes. So evaluating primeFactors 9 would follow this process:

  1. Evaluate factor 9 primes.
  2. Ask primes for its first element, which is 2.
  3. Ask primes for its next element.
  4. As part of this process, evaluate primeFactors 3.
  5. Ask primes for its first element, which is 2.
  6. Ask primes for its next element.
  7. As part of this process, evaluate primeFactors 3.
  8. ...

In other words, steps 2-4 would repeat infinitely. Clearly I am mistaken, as the algorithm terminates. What mistake am I making here?

like image 969
Matthew Avatar asked Dec 12 '11 03:12

Matthew


1 Answers

primeFactors only ever reads up to the square root of the number it's evaluating. It never looks further in the list, which means it never "catches up" to the number it's testing for inclusion in the list. Because Haskell is lazy, this means that the primeFactors test does terminate.

The other thing to remember is that primes isn't a function that evaluates to a list each time you access it, but rather a list that's constructed lazily. So once the 15th element has been accessed once, accessing it a second time is "free" (e.g. it doesn't require any further calculation).

like image 113
Lily Ballard Avatar answered Nov 02 '22 21:11

Lily Ballard