繁体   English   中英

如何处理Haskell中的类型

[英]How to deal with types in Haskell

我已经开始学习Haskell,并且一直在阅读“学习Haskell带来的好处”。 我阅读了“模块”一章的一半内容。 一位朋友向我展示了代码战,我决定将我学到的一些东西进行测试。

我正在尝试制作一个函数,该函数返回一个布尔值,该布尔值是否为给定的Integral是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函数的类型签名,因为这是代码战提供的,所以我猜想这是必需的。

在Haskell中, /运算符用于分数类型,而不是整数类型。 对于整数类型,应使用div (您可以通过将其括在反引号中来使用infix)。 您还可以使用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

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

更好的方法是使用@Jubobs建议的整数除法和取模。

div是整数除法,它为您提供除法结果,而无余数。 这与您对truncate (x / 4)期望相同。 应将其重写为div x 4 ,因此它不依赖浮点数。

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

哈斯克尔永远, 永远不会隐式类型转换。 对于大多数程序员来说,这似乎有些固执,但这实际上是Haskell的类型系统如此出色的原因之一。 完全回避的转换使许多事情变得更加简单,这是完全双向类型推断的前提。

在您的情况下,问题出在x / 4 除法,您似乎希望结果可能不是整数。 但是它必须是整数,只要它具有整数类型1即可 因此,您需要先明确转换为分数类型2 实际上,最简单的方法是您已经发现的fromIntegral函数:

   if isWhole $ fromIntegral x / 4 then ...

明确说明:这就像if isWhole ( (fromIntegral x)/4 ) then解析一样。 在这一点上,您可能会怀疑:如果我只是说过Haskell从来没有隐式地转换东西,那为什么用整数4来除法还可以呢? 答案是,Haskell没有整数文字! 它只有各种数字文字 整数文字没有任何特定的类型,它是多态的(即,它从上下文中请求此处应使用哪种类型 ,并且如果该类型位于Num类中,则该行为类似于该类型)。


1 C通过使整数除以trutru结果来“解决”该问题……这导致了很多问题,可能比隐式转换要多。

2 “某些分数类型”在Haskell中表示默认为Double 这种类型非常快,但也不是完全没有问题,因为浮点数本质上是不精确的。 我现在不确定这是否会影响您的应用程序; 由于==比较,它很可能是。 基本上,您需要指定isWhole的类型来防止这种情况,例如isWhole :: Rational -> Bool Rational类型将非整数表示为精确分数。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM