第一天学习haskell,来自python背景我在调试类型方面遇到了麻烦。目前,我正在开发一个简单的函数,以查看数字是否为素数;
prime p = if p == 1 then False else if p == 2 then True else if maximum ([if p `mod` x == 0 then x else -1 | x<-[2..(floor(p**0.5))]]) > 0 then False else True
当我有一个特定的数字而不是通用的P时它起作用,但无论我尝试什么(我已经尝试了很多,包括只是转移到不同的问题)我总是得到某种关于类型的错误。对于当前的迭代,我得到了错误
<interactive>:149:1: error:
* Ambiguous type variable `a0' arising from a use of `prime'
prevents the constraint `(RealFrac a0)' from being solved.
Probable fix: use a type annotation to specify what `a0' should be.
These potential instances exist:
instance RealFrac Double -- Defined in `GHC.Float'
instance RealFrac Float -- Defined in `GHC.Float'
...plus one instance involving out-of-scope types
(use -fprint-potential-instances to see them all)
* In the expression: prime 2
In an equation for `it': it = prime 2
<interactive>:149:7: error:
* Ambiguous type variable `a0' arising from the literal `2'
prevents the constraint `(Num a0)' from being solved.
Probable fix: use a type annotation to specify what `a0' should be.
These potential instances exist:
instance Num Integer -- Defined in `GHC.Num'
instance Num Double -- Defined in `GHC.Float'
instance Num Float -- Defined in `GHC.Float'
...plus two others
...plus one instance involving out-of-scope types
(use -fprint-potential-instances to see them all)
* In the first argument of `prime', namely `2'
In the expression: prime 2
In an equation for `it': it = prime 2
如果有人能够,以及调试这个特定的程序,让我了解如何考虑haskell类型,我会非常感激。我试过看看learnyouahaskell,但到目前为止我还没有运气。
答案 0 :(得分:4)
简而言之:同时使用mod
,floor
和(**)
,您可以限制p
a的类型很多,并且Haskell找不到要调用prime
的数字类型。
这里的主要问题是你的列表理解的可迭代:
[2..(floor(p**0.5))]
您可以在此致电p ** 0.5
,但由于(**)
的类型为(**) :: Floating a => a -> a -> a
,因此p
必须是类型的实例,而该类型的实例是Floating
类型类,例如Float
。我想你不想那样。
您的floor :: (RealFrac a, Integral b) => a -> b
甚至会让情况变得更糟,因为现在p
也必须属于RealFrac
类型类的实例。
另一方面,您使用mod :: Integral a => a -> a -> a
,这意味着您的p
必须是Floating
,而Integral
则是两个析取集合:虽然严格来说,我们可以定义这样的类型,但是同一类型的Integral
和Floating
数字相当奇怪。 Float
例如是Floating
个号码,但不是Integral
,而Int
是Integral
,而不是Floating
类型。
我们必须找到一种方法来放松p
上的约束。由于通常非Integral
数字根本不是素数,因此我们最好的目标是抛弃floor
和(**)
。然而,迭代到p
的平方根的优化是一个好主意,但我们需要找到其他方法来强制执行。
执行此操作的一种方法是使用takeWhile :: (a -> Bool) -> [a] -> [a]
我们获取元素,直到数字的平方大于p
,因此我们可以将[2..(floor(p**0.5))]
重写为:< / p>
takeWhile (\x -> x * x <= p) [2..]
我们甚至只能使用奇数元素和2
,将其写成:
takeWhile (\x -> x * x <= p) (2:[3, 5..])
如果我们使用p
进行测试,例如设置为99
,我们会得到:
Prelude> takeWhile (\x -> x * x <= 99) (2:[3, 5..])
[2,3,5,7,9]
如果我们将其插入,我们放宽了类型:
prime p = if p == 1 then False else if p == 2 then True else if maximum ([if p `mod` x == 0 then x else -1 | x <- takeWhile (\x -> x * x <= p) (2:[3, 5..])]) > 0 then False else True
我们实际上放松了它:
Prelude> :t prime
prime :: Integral a => a -> Bool
我们得到:
Prelude> prime 23
True
但代码非常丑陋而非 un-Haskell 。首先,您在此处使用maximum
作为检查所有元素是否满足谓词的技巧。但这样做是没有意义的:从其中一个元素可分割的那一刻起,我们知道这个数字不是素数。所以我们可以更好地使用all :: (a -> Bool) -> [a] -> Bool
函数。此外,通常使用模式匹配和警卫来检查条件,因此我们可以像下面那样编写:
prime :: Integral a => a -> Bool
prime n | n < 2 = False
| otherwise = all ((/=) 0 . mod n) divisors
where divisors = takeWhile (\x -> x * x <= n) (2:[3, 5..])
答案 1 :(得分:1)
您的代码可以简化为
prime p = if p == 1 then False else
if p == 2 then True else
if maximum ([if p `mod` x == 0 then x else -1 | x<-[2..(floor(p**0.5))]]) > 0
then False else True
=
prime p = if p == 1 then False else
if p == 2 then True else
not (maximum [if p `mod` x == 0 then x else -1 | x<-[2..floor(p**0.5)]] > 0 )
=
prime p = not ( p == 1 ) &&
( p == 2 ||
maximum [if p `mod` x == 0 then x else -1 | x<-[2..floor(p**0.5)]] <= 0 )
=
prime p = p /= 1 &&
( p == 2 ||
maximum [if p `mod` x == 0 then x else -1 | x<-[2..floor(p**0.5)]] == -1 )
=~
prime p = p == 2 || p > 2 && null [x | x <- [2..floor(p**0.5)], p `mod` x == 0]
(说服自己在每次转型的有效性方面)。
这当然会给我们一个类型错误,因为(**) :: Floating a => a -> a -> a
和mod :: Integral a => a -> a -> a
是冲突的。要解决这个问题,只需在其中抛出fromIntegral
:
isPrime :: Integral a => a -> Bool
isPrime p = p == 2 ||
p > 2 && null [x | x <- [2..floor(fromIntegral p**0.5)], p `mod` x == 0]
它正在运作:
~> filter isPrime [1..100]
[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97]