如何处理Haskell中的类型

时间:2015-03-04 23:46:48

标签: haskell types

我已经开始学习Haskell,而且我一直在阅读“为了大好学而学习哈斯克尔”。我读完了模块章节的一半。一位朋友向我展示了代码战,我决定将我学到的一些内容用于测试。

我正在尝试创建一个函数,该函数返回给定积分是否为4的幂的布尔值。这是代码。

module PowerOfFour where

isWhole n = fromIntegral (round n) == n

isPowerOf4 :: Integral n => n -> Bool
isPowerOf4 4 = True
isPowerOf4 x = if x < 4 then
    False
else
    if isWhole (x / 4) then
    isPowerOf4 (truncate (x / 4))
  else
    False

这是我收到的错误消息。

/tmp/haskell11524-8-kke52v/PowerOfFour.hs:10:12:
    Could not deduce (RealFrac n) arising from a use of `isWhole'
    from the context (Integral n)
      bound by the type signature for
                 isPowerOf4 :: Integral n => n -> Bool
      at /tmp/haskell11524-8-kke52v/PowerOfFour.hs:5:15-37
    Possible fix:
      add (RealFrac n) to the context of
        the type signature for isPowerOf4 :: Integral n => n -> Bool
    In the expression: isWhole (x / 4)
    In the expression:
      if isWhole (x / 4) then isPowerOf4 (truncate (x / 4)) else False
    In the expression:
      if x < 4 then
          False
      else
          if isWhole (x / 4) then isPowerOf4 (truncate (x / 4)) else False

/tmp/haskell11524-8-kke52v/PowerOfFour.hs:10:23:
    Could not deduce (Fractional n) arising from a use of `/'
    from the context (Integral n)
      bound by the type signature for
                 isPowerOf4 :: Integral n => n -> Bool
      at /tmp/haskell11524-8-kke52v/PowerOfFour.hs:5:15-37
    Possible fix:
      add (Fractional n) to the context of
        the type signature for isPowerOf4 :: Integral n => n -> Bool
    In the first argument of `isWhole', namely `(x / 4)'
    In the expression: isWhole (x / 4)
    In the expression:
      if isWhole (x / 4) then isPowerOf4 (truncate (x / 4)) else False

我做错了什么?

我在isWhole中使用fromIntegral来修复类似的错误,并且isWhole现在可以单独使用。但是,我似乎无法摆脱isPowerOf4中的这些错误。我已经尝试过GHC提供的可能修复程序,但我可能做得不对。

我宁愿保留isPowerOf4函数的类型签名,因为这是由代码新闻提供的,所以我猜这是一个要求。

3 个答案:

答案 0 :(得分:5)

在Haskell中,/运算符用于小数类型而非整数类型。对于整数类型,您应该使用div(您可以通过在反引号中包围它来使用它作为中缀)。您还可以使用modrem查找剩余部分,例如%的典型语言(负数不同)

isPowerOf4 :: Integral n => n -> Bool
isPowerOf4 4 = True
isPowerOf4 x = if x < 4
  then False
  else
    if x `mod` 4 == 0
      then isPowerOf4 (x `div` 4)
      else False

答案 1 :(得分:4)

由于@leftaroundabout解释/是浮点除法,结果可能是舍入误差的对象。因此,isWhole函数无法保证在所有情况下都能正常工作。

更好的方法是使用整数除法和模数,如@Jubobs所示。

div是整数除法,它为你提供除法余数的除法结果。这与您对truncate (x / 4)的期望相同。这应该被重写为div x 4,所以它不依赖于浮点数。

对于isWhole,你可以使用modulo这是整数除法的其余部分。 (例如mod 5 4 == 1)如果它为零则没有余数,结果是整数。您可以使用isWhole (x / 4)代替mod x 4 == 0divmod都可以正常工作,因此不存在舍入错误的危险。

答案 2 :(得分:3)

Haskell从不,永远确实会影响类型转换。对于大多数程序员来说,这似乎相当顽固,但实际上这也是Haskell类型系统如此有效的原因之一。完全避免转换会使许多事情变得更加简单,而且这是完全双向类型推断的先决条件。

在您的情况下,问题出在x / 4。分裂,你似乎期望结果可能是非整数。但它必须是整数,只要它有一个整数类型 1 !因此,您需要首先显式转换为小数类型 2 。最简单的方法确实是您已经发现的fromIntegral函数:

   if isWhole $ fromIntegral x / 4 then ...

要说清楚:将其解析为if isWhole ( (fromIntegral x)/4 ) then。此时你可能想知道:如果我只是说Haskell从不隐式转换东西,那么为什么除以整数文字4可以呢?答案是,Haskell没有整数文字!它只有各种数字文字。整数文字不具有任何特定类型,它是多态的(即,它从上下文请求此处预期的类型,并且表现为该类型 - 前提是该类型在Num类中。


1 C“解决”这个问题,通过使整数除法结果结果......导致一系列问题,可能比你通过隐式转换得到的更多

2 “某些小数类型”意味着,在Haskell中,它默认为Double。这种类型非常快,但它也不是完全没有问题的,因为浮点数本质上是不精确的。如果这在您的申请中存在问题,我现在还不确定;因为它可能是==的比较。基本上,您需要指定isWhole的类型以防止这种情况,如isWhole :: Rational -> BoolRational类型将非整数表示为精确分数。