[英]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)。 您还可以使用mod
或rem
查找余数,例如典型语言中的%
(对于负数,它们是不同的)
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)
。 div
和mod
可以精确工作,因此没有舍入错误的危险。
哈斯克尔永远, 永远不会隐式类型转换。 对于大多数程序员来说,这似乎有些固执,但这实际上是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.