Here is my own Haskell trial. I'm still a Haskell newbie myself (but not really a newbie as a functional programmer), hence there is probably many possible improvements. I could probably find some myself thinking harder, but it works as expected and I see no obvious algorithmic slowness.
The code includes a primes number generator as one was necessary for a complete answer. OP could have provided his one primes generator as he said he know how to do it, it would have avoided including it in answers.
I don't see much difference between what I wrote functionaly using Haskell and what I could have wrote iteratively. The main difficulty was probably the strange expected format of the result.
module Primes
where
isPrime :: Integer -> Bool
isPrime a = isPrimeHelper a primes
isPrimeHelper a (p:ps)
| p*p > a = True
| a `mod` p == 0 = False
| otherwise = isPrimeHelper a ps
-- List of prime numbers
primes :: [Integer]
primes = 2 : filter isPrime [3..]
countFactor x p
| x `mod` p == 0 = 1 + countFactor (x `div` p) p
| otherwise = 0
cleanFactor x p
| x `mod` p == 0 = cleanFactor (x `div` p) p
| otherwise = x
nbFactors x (p:ps)
| x == 1 = []
| x `mod` p == 0 = (countFactor x p) : (nbFactors (cleanFactor x p) ps)
| otherwise = 0 : (nbFactors x ps)
-- List of number of times each prime number divides x
-- (tricky because the result is supposed to put 0 for non dividers primes,
-- but only for those smaller than larger prime divider...)
factor :: Integer -> [Integer]
factor x = nbFactors x primes
Unit tests are included below. As I use TDD I had to write them anyway.
module Main
where
import Test.HUnit
import Primes
test1 = True ~=? (isPrime 2)
test2 = True ~=? (isPrime 3)
test3 = False ~=? (isPrime 4)
test4 = True ~=? (isPrime 5)
test5 = 2 ~=? (primes !! 0)
test6 = 7 ~=? (primes !! 3)
test7 = 2 ~=? (countFactor 825 5)
test8 = 0 ~=? (countFactor 825 17)
test9 = 33 ~=? (cleanFactor 825 5)
test10 = [0, 1, 2, 0, 1] ~=? (factor 825)
tests = TestList [ test1, test2, test3, test4,
test5, test6,
test7, test8, test9,
test10
]
main = do runTestTT tests
Below is a better version suggested by yatima2975 that needs less calls to mod and div (that can be quite costly whith large numbers).
module Primes
where
isPrime :: Integer -> Bool
isPrime = isPrimeHelper primes
isPrimeHelper (p:ps) a
| p*p > a = True
| a `mod` p == 0 = False
| otherwise = isPrimeHelper ps a
-- List of prime numbers
primes :: [Integer]
primes = 2 : filter isPrime [3..]
countClean = countCleanHelper 0
countCleanHelper e x p = case divMod x p of
(x',0) -> countCleanHelper (e+1) x' p
_ -> (e,x)
nbFactors _ 1 = []
nbFactors (p:ps) x = let (exponent, rest) = countClean x p
in exponent : nbFactors ps rest
-- List of number of times each prime number divides x
-- (tricky because the result is supposed to put 0 for non dividers primes,
-- but only for those smaller than larger prime divider...)
factor :: Integer -> [Integer]
factor = nbFactors primes
And the updated tests
module Main
where
import Test.HUnit
import Primes
test1 = True ~=? (isPrime 2)
test2 = True ~=? (isPrime 3)
test3 = False ~=? (isPrime 4)
test4 = True ~=? (isPrime 5)
test5 = 2 ~=? (primes !! 0)
test6 = 7 ~=? (primes !! 3)
test7 = (2,33) ~=? (countClean 825 5)
test8 = (0,825) ~=? (countClean 825 17)
test10 = [0, 1, 2, 0, 1] ~=? (factor 825)
tests = TestList [ test1, test2, test3, test4,
test5, test6,
test7, test8,
test10
]
main = do runTestTT tests