Reputation: 15471
So I've decided to toy around with Haskell. I am attempting to solve the very first problem on Project Euler. The following is my code:
euler1 limit (div:divisors) = if limit > 1 then (euler1 limit divisors) + (euler1 limit (div:[])) + (euler1 (limit-1) (div:divisors)) else 0
euler1 limit (divisor:[]) = if limit > 1 && (mod limit divisor) == 0 then limit else 0
euler1 limit [] = 0
However, when I run this through ghci, the following happens:
euler1 9 [3,5]
*** Exception: <interactive>:3:5-90: Non-exhaustive patterns in function euler1
Further debugging:
euler1 5 []
0
euler1 5 [5]
*** Exception: <interactive>:3:5-90: Non-exhaustive patterns in function euler1
This suggests that the broken code is in the second case (list with one element), where euler1 does not even contain a recursive step.
Whats going on? Why is it breaking quite so spectacularly? What is the pattern that I've missed? (I've got single element list, multi element list, and empty list no?)
EDIT: For anybody who cares the solution I initially provided above (with John Ls brilliant help) is still not quite right as it will count items which are multiples of more then one divisor more then once. A final, correct, working algorithm is the following:
euler1 limit (divisor:[]) = if ((limit > 1) && ((mod limit divisor) == 0)) then limit else 0
euler1 limit (div:divisors) | ((limit > 1) && (euler1 limit (div:[]))==0) = (euler1 limit divisors) + (euler1 (limit-1) (div:divisors)) | ((limit > 1) && (euler1 limit (div:[]))/=0) = (euler1 limit (div:[])) + (euler1 (limit-1) (div:divisors)) |limit > 1 = euler1 (limit-1) (div:divisors) | otherwise = 0
euler1 limit [] = 0
Upvotes: 2
Views: 149
Reputation: 28097
are you defining this in ghci? If you do:
Prelude> let euler1 limit (divisor:[]) = if limit > 1 && (mod limit divisor) == 0 then limit else 0
Prelude> let euler1 limit [] = 0
the second definition will shadow the first, causing the non-exhaustive pattern failure.
Instead, you should use ghci's multi-line syntax
Prelude> :{
Prelude| let euler1 limit (divisor:[]) = if limit > 1 && (mod limit divisor) == 0 then limit else 0
Prelude| euler1 limit (div:divisors) = if limit > 1 then (euler1 limit divisors) + (euler1 limit (div:[])) + (euler1 (limit-1) (div:divisors)) else 0
Prelude| euler1 limit [] = 0
Prelude| :}
Also note that I switched the order of the top two statements. div:divisors
will match a single-element list, so you need to check that special case first.
Upvotes: 10