与 Haskell 类型系统作斗争

Struggling with Haskell type system

我正在尝试通过在特定情况下执行基本任务来学习一些 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

我真的可以在这里得到一些帮助,提前谢谢你。

你的代码有两个问题:

  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

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

的事情
where m = floor . sqrt . fromIntegral $ n

另一种选择是将 [1..m] 替换为 takeWhile (\x -> x * x <= n) [1..] 之类的内容,以避免需要 Floating.

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

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

事实证明,虽然除法成本很高,但它经常会产生两个结果:商和余数。对于 mod,您正在寻找后者,但我们有 divMod and quotRem which produce both. So it could be worth testing if that's notably slower than the plain mod test (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]