问题
I'm new to Haskell, and I'm trying a bit:
isPrime :: Integer->Bool
isPrime x = ([] == [y | y<-[2..floor (sqrt x)], mod x y == 0])
I have a few questions.
- Why when I try to load the .hs, WinHugs say: Instances of
(Floating Integer, RealFrac Integer)
required for definition ofisPrime
? - When the interpreter finds one element in the right set, it immediately stops or it computes all the set? I think you know what I mean.
Sorry about my english.
回答1:
1) The problem is that sqrt
has the type (Floating a) => a -> a
, but you try to use an Integer as argument. So you have to convert your Integer first to a Floating, e.g. by writing sqrt (fromIntegral x)
2) I see no reason why == shouldn't be lazy, but for testing for an empty collection you can use the null
function (which is definitely lazy, as it works on infinite lists):
isPrime :: Integer->Bool
isPrime x = null [y | y<-[2..floor (sqrt (fromIntegral x))], x `mod` y == 0]
But in order to get an more idiomatic solution, break the problem into smaller sub-problems. First, we need a list of all elements y with y*y <= x:
takeWhile (\y -> y*y <= x) [2..]
Then we need only the elements that divide x:
filter (\y -> x `mod`y == 0) (takeWhile (\y -> y*y <= x) [2..])
Then we need to check if that list is empty:
isPrime x = null (filter (\y -> x `mod`y == 0) (takeWhile (\y -> y*y <= x) [2..]))
And if this looks to lispy to you, replace some of the parens with $
isPrime x = null $ filter (\y -> x `mod` y == 0) $ takeWhile (\y -> y*y <= x) [2..]
For additional clarity you can "outsource" the lambdas:
isPrime x = null $ filter divisible $ takeWhile notTooBig [2..] where
divisible y = x `mod`y == 0
notTooBig y = y*y <= x
You can make it almost "human readable" by replacing null $ filter with not $ any:
isPrime x = not $ any divisible $ takeWhile notTooBig [2..] where
divisible y = x `mod`y == 0
notTooBig y = y*y <= x
回答2:
Because
sqrt
has the typeFloating a => a -> a
. This means the input has to be aFloating
type and the output will be the same type. In other wordsx
needs to be aFloating
type. However you declaredx
to be of typeInteger
, which is not aFloating
type. In additionfloor
needs aRealFrac
type, sox
needs to be that as well.The error message suggests that you fix that by making
Integer
aFloating
type (by defining an instanceFloating Integer
(and the same forRealFrac
).Of course this is not the correct approach in this case. Rather you should use
fromIntegral
to convertx
to aReal
(which is an instance ofFloating
andRealFrac
) and then give that tosqrt
.Yes. As soon as
==
sees that the right operand has at least one element, it knows it is not equal to[]
and thus returnsFalse
.That being said,
null
is a more idiomatic way to check whether a list is empty than[] ==
.
回答3:
Regarding the second point, it stops, for example:
[] == [x | x <- [1..]]
Returns False
回答4:
Landei's solution is great, however, if you want a more efficient¹ implementation we have (thanks to BMeph):
-- list of all primes
primes :: [Integer]
primes = sieve (2 : 3 : possible [1..]) where
sieve (p : xs) = p : sieve [x | x <- xs, x `mod` p > 0]
possible (x:xs) = 6*x-1 : 6*x+1 : possible xs
isPrime :: Integer -> Bool
isPrime n = shortCircuit || (not $ any divisible $ takeWhile inRangeOf primes) where
shortCircuit = elem n [2,3] || (n < 25 && ((n-1) `mod` 6 == 0 || (n+1) `mod` 6 == 0))
divisible y = n `mod` y == 0
inRangeOf y = y * y <= n
The 'efficiency' comes from the use of constant primes. It improves the search in two ways:
- The Haskell runtime could cache the results so subsequent invocations are not evaluated
- It eliminates a range of numbers by logic
note that the
sieve
value is simply a recursive table, where says the head of the list is prime, and adds it to it. For the rest of the lists if there is no other value already in the list that composes the number then its also primepossible
is list of all possible primes, since all possible primes are in the form 6*k-1 or 6*k-1 except 2 and 3 The same rule is applied forshortCircuit
too to quickly bail out of calculations
Footnote by D.F.
¹ It's still a terribly inefficient way to find primes. Don't use trial division if you need primes larger than a few thousand, use a sieve instead. There are several far more efficient implementations on hackage.
回答5:
- I think WinHugs needs to import a module for Integer and etc... Try Int
- The interpreter will not compute anything until you call e.g.
isPrime 32
then it will lazily compute the expression.
PS your isPrime implementation is not the best implementation!
来源:https://stackoverflow.com/questions/4541415/haskell-prime-test