【问题标题】:What is the type signature of this Haskell function?这个 Haskell 函数的类型签名是什么?
【发布时间】:2010-10-27 12:38:30
【问题描述】:

我写了一个函数来检查一个数是否是素数:

prime n = prime' n 2 (floor (sqrt n))
     where prime' n c u | n `mod` c == 0 = False
                        | c > u = True
                        | otherwise = prime' n (c+1) u

我不知道这个函数的类型签名应该是什么。一开始我以为应该是这样的:

prime :: Integral a => a -> Bool

但是编译时出现错误,因为sqrt 需要Floating a,而floor 需要RealFrac a 而不是Integral a。当我删除类型签名时,它会编译,但函数不起作用:

*Euler> :t prime
prime :: (Integral a, RealFrac a, Floating a) => a -> Bool
*Euler> prime 5

<interactive>:1:0:
    Ambiguous type variable `t' in the constraints:
      `Floating t' arising from a use of `prime' at <interactive>:1:0-6
      `RealFrac t' arising from a use of `prime' at <interactive>:1:0-6
      `Integral t' arising from a use of `prime' at <interactive>:1:0-6
    Probable fix: add a type signature that fixes these type variable(s)

我怎样才能使这个功能起作用?

【问题讨论】:

  • 哈,总理',大声读出来。 ;-)
  • 先把它读成撇号,然后我才意识到素数:)

标签: haskell types type-inference


【解决方案1】:

问题是您在n 上使用sqrt,这会强制n 成为浮点数;并且您还在n 上使用mod,它强制n 为整数。直观上看你的代码,n应该是一个整数,所以你不能直接调用sqrt。相反,您可以使用 fromIntegral 之类的东西将其从整数转换为另一种数字类型。

prime :: (Integral a) => a -> Bool
prime n = prime' n 2 (floor (sqrt (fromIntegral n)))
     where prime' n c u | n `mod` c == 0 = False
                        | c > u = True
                        | otherwise = prime' n (c+1) u

【讨论】:

    【解决方案2】:

    只是回顾一下其他答案没有涵盖的最后一点......

    *Euler> :t prime
    prime :: (Integral a, RealFrac a, Floating a) => a -> Bool
    

    类型检查器推断prime 可以接受a 类型的参数,只要aIntegralRealFracFloating 类的一个实例。

    *Euler> prime 5
    
    <interactive>:1:0:
        Ambiguous type variable `t' in the constraints:
          `Floating t' arising from a use of `prime' at <interactive>:1:0-6
          `RealFrac t' arising from a use of `prime' at <interactive>:1:0-6
          `Integral t' arising from a use of `prime' at <interactive>:1:0-6
        Probable fix: add a type signature that fixes these type variable(s)
    

    但是,当您向prime 5 询问时,它会抱怨5 的默认类型都不能满足这些条件。

    你也可以自己写

    instance (Integral a, RealFrac b, Floating b) => Integral (Either a b) where ...
    instance (Integral a, RealFrac b, Floating b) => RealFrac (Either a b) where ...
    instance (Integral a, RealFrac b, Floating b) => Floating (Either a b) where ...
    

    (您还必须添加NumOrdRealFractional 等实例),然后prime 5 是可以接受的,因为存在5 :: Either Integer Float满足类型条件。

    【讨论】:

      【解决方案3】:

      或者,您可以更改上限测试:

      prime n = prime' n 2
          where prime' n c | n `mod` c == 0 = False
                           | c * c > n = True
                           | otherwise = prime' n (c+1)
      

      顺便说一句,您不需要 n 作为 prime' 的参数,因为它在所有调用中都是不变的。

      【讨论】:

      • 进一步微优化:prime n = prime' 2 4 where prime' c s | n mod` c == 0 = False | s > n = 真 |否则 = prime' (succ c) (s+c+c+1)`:即通过使用“n^2 = 1 + 3 + .. + (2*n-1)”来避免乘法。不过这可能不值得:)
      【解决方案4】:

      您可以将(sqrt n) 更改为(sqrt (fromInteger n)) 以使该功能按预期工作。这是必需的,因为sqrt 的类型是:

      sqrt :: (Floating a) => a -> a
      

      所以这是错误的,例如,这样做:

      sqrt (2 :: Int)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-06-28
        • 2021-05-12
        • 1970-01-01
        • 2018-02-06
        • 2011-01-11
        • 2013-08-20
        相关资源
        最近更新 更多