Another Haskell exercise

Posted on April 15, 2012

A few days ago there was a link on reddit: some company asked applicants to send resume to an address that is the first seven-digit Prime number taken from the consecutive digits of Pi which is also a palindrome. I cannot find the link anymore, but I wrote this just for fun:

> module Main where

Generate digits of Pi as an infinite list.
Use Unbounded Spigot Algorithm from this article:
http://www.cs.ox.ac.uk/jeremy.gibbons/publications/spigot.pdf

> piDigits :: [Integer]
> piDigits = g(1,0,1,1,3,3) where
> g(q,r,t,k,n,l) = if 4*q+r-t<n*t
>   then n : g(10*q,10*(r-n*t),t,k,div(10*(3*q+r))t-10*n,l)
>   else g(q*k,(2*q+r)*l,t*l,k+1,div(q*(7*k+2)+r*l)(t*l),l+2)

Find palindromes of specified length in an infinite list of digits.
Result is returned as an infinite list of Integers, each element
a palindrome when represented as digital characters. E.g.:
"findPali 3 [1,2,3,2,1,7,9,7]" returns "[232,797]".

> findPali :: Int -> [Integer] -> [Integer]
> findPali n x
>   | length pref < n      = []  -- Not necessary for infinite lists
>   | pref == reverse pref = toint pref:findPali n (tail x)
>   | otherwise            = findPali n (tail x)
>     where
>       pref = take n x
>       toint = foldl (\acc x -> acc*10+x) 0

Check if an Integer is a Prime number. Function copied from here:
http://stackoverflow.com/questions/4541415/haskell-prime-test
It is a "naive" implementaion but fast enough for our case, and
readily understandable.

> isPrime :: Integer -> Bool
> isPrime x = not $ any divisible $ takeWhile notTooBig [2..]
>   where
>     divisible y = x `mod` y == 0
>     notTooBig y = y*y <= x

Find and return the first element in a list based on predicate.
Our list is infinite so we don't need the result to be of "Maybe"
type like the "canonical" implementation in Data.List.

> find' :: (a -> Bool) -> [a] -> a
> find' p (x:xs)
>   | p x       = x
>   | otherwise = find' p xs

Print the first 7-digit palindrome of consecutive digits of Pi
that is a Prime number.

> main = putStrLn $ show $ find' isPrime $ findPali 7 piDigits

This is not the fastest thing you can invent (prime check is very simplistic), but it gives the result in under a minute on my system.

UPDATE 2012-04-16: Found the link. Not writing it here ;)