与Haskell类型系统斗争

时间:2018-07-02 19:46:59

标签: haskell types sqrt

在特定情况下,我试图通过执行基本任务来学习一些Haskell,我试图实现一些用于素数检查的基本功能,但是我真的可以弄清楚类型,我的代码是

isPrime :: (Num a) => a -> Bool
isPrime n
    | n <= 1 = False
    | otherwise = not $ 0 `elem` map (mod n) [2..m] where m = floor $ sqrt n

我尝试代替(Num a) => a使用不同的数字类型或将sqrtfromIntegral一起使用,但仍然收到错误消息,例如:

*Could not deduce (Floating a) arising from a use of `sqrt'
  from the context: Num a
    bound by the type signature for:
               isPrime :: forall a. Num a => a -> Bool
    at Helpers.hs:5:1-31
  Possible fix:
    add (Floating a) to the context of
      the type signature for:
        isPrime :: forall a. Num a => a -> Bool
* In the second argument of `($)', namely `sqrt n'
  In the expression: floor $ sqrt n
  In an equation for `m': m = floor $ sqrt n
| otherwise = not $ 0 `elem` map (mod n) [2..m] where m = floor $ sqrt n

我真的可以在这里使用一些帮助,谢谢。

3 个答案:

答案 0 :(得分:4)

正如其他人所提到的,使用mod要求aIntegral,使用sqrt要求a为{{1 }}。从函数的名称来看,我假设您要在整数类型上使用它。 因此,您可以通过以下方法解决此问题:将签名更改为Floating,然后用isPrime :: (Integral a) => a -> Bool预先组成sqrt。你可以做类似的事情

fromIntegral

另一种选择是将where m = floor . sqrt . fromIntegral $ n 替换为[1..m],以避免需要takeWhile (\x -> x * x <= n) [1..]

答案 1 :(得分:3)

您的代码中有两个问题:

  1. 不兼容的类型。

同时调用sqrt n(mod n)都要求n分别为FloatingIntegral

  1. 上下文不足。仅要求(Num a)不允许进行任何两项操作。

可能的解决方法是:a)将类型上下文范围缩小到更简洁的(Integral a); b)将fromIntegral添加到sqrt的参数中:

isPrime :: Integral a => a -> Bool
isPrime n
    | n <= 1 = False
    | otherwise = not $ 0 `elem` map (mod n) [2..m] where m = floor $ sqrt $fromIntegral n

答案 2 :(得分:1)

编译器描述的问题是您将不兼容的操作应用于同一类型:mod要求Integral asqrt要求Floating a,并且没有类型可以满足都。您可以使用fromIntegralceiling之类的类型转换来解决此问题,但是您要小心避免舍入错误。在测试中,我删除了类型约束并使用了m = ceiling $ sqrt $ fromIntegral n,这导致了推断出的类型isPrimeSqrt :: Integral a => a -> Bool

另一种方法是考虑为什么会发生冲突并寻找其他解决方案。 sqrt的原因是为测试产生优化的停止点。我们可以用另一种方式找到那个停止点吗?

事实证明,尽管除法很昂贵,但它经常产生两个结果:商和余数。使用mod,您需要寻找后者,但是我们同时拥有divModquotRem。因此,如果这比普通的mod测试(benchmark results(将[2..][2..m]进行比较)显着慢,则值得进行测试。

isPrime n = (n > 1) && null (filter isFactor (takeWhile notTooHigh divisors))
  where notTooHigh (divisor,quotient,_) = divisor <= quotient
        isFactor (_,_,remainder) = remainder == 0
        divisors = [(divisor,quotient,remainder) |
                    divisor <- 2:[3,5..],
                    let (quotient,remainder) = quotRem n divisor]