Haskell类型错误

时间:2018-05-09 13:31:23

标签: haskell types compiler-errors primes typeclass

第一天学习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,但到目前为止我还没有运气。

2 个答案:

答案 0 :(得分:4)

简而言之:同时使用modfloor(**),您可以限制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则是两个析取集合:虽然严格来说,我们可以定义这样的类型,但是同一类型的IntegralFloating数字相当奇怪。 Float例如是Floating个号码,但不是Integral,而IntIntegral,而不是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 -> amod :: 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]