如何处理 Haskell 中的类型
How to deal with types in Haskell
我已经开始学习 Haskell 并且一直在阅读 "Learn You a Haskell for Great Good"。我读了一半的模块章节。一位朋友向我展示了 codewars,我决定将我学到的一些知识用于测试。
我正在尝试创建一个函数,该函数 returns 是一个布尔值,用于判断给定的 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 函数的类型签名,因为它是由 codewars 提供的,所以我猜这是一个要求。
Haskell 从不,曾经 进行隐式类型转换。这对大多数程序员来说似乎相当顽固,但实际上这是 Haskell 的类型系统如此出色的原因之一。完全避免转换使很多事情变得简单得多,并且这是完全双向类型推断的先决条件。
你的情况,问题出在x / 4
。一个除法,你似乎期望结果可能是非整数。但是必须是整数,只要是整数类型1即可!所以需要先显式转换为小数类型2。最简单的方法确实是您已经发现的 fromIntegral
函数:
if isWhole $ fromIntegral x / 4 then ...
为了清楚起见:它被解析为 if isWhole ( (fromIntegral x)/4 ) then
。此时你可能想知道:如果我只是说 Haskell 从不隐式转换东西,那么为什么除以整数文字 4
就可以了?答案是 Haskell 没有整数文字!它只有各种 数字文字 。整数文字没有任何特定类型,它是多态的(即,它从上下文请求此处预期的类型,并且比该类型表现得更好——前提是类型在 Num
class).
1C 通过对结果进行整数除法截断来“解决”这个问题......这会导致大量问题,可能比你更多d 通过隐式转换得到。
2“一些小数类型”的意思是,在Haskell中,它会默认为Double
。这种类型非常快,但也不是完全没有问题,因为浮点数本质上是不精确的。我现在不确定这是否会成为您应用程序中的问题;因为 ==
比较,它很可能是。基本上,您需要指定 isWhole
的类型以防止出现这种情况,如 isWhole :: Rational -> Bool
。 Rational
类型将非整数表示为精确分数。
在 Haskell 中,/
运算符用于小数类型而不是整数类型。对于整数类型,你应该使用 div
(你可以通过用反引号包围它来使用它中缀)。您也可以使用 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 并且一直在阅读 "Learn You a Haskell for Great Good"。我读了一半的模块章节。一位朋友向我展示了 codewars,我决定将我学到的一些知识用于测试。
我正在尝试创建一个函数,该函数 returns 是一个布尔值,用于判断给定的 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 函数的类型签名,因为它是由 codewars 提供的,所以我猜这是一个要求。
Haskell 从不,曾经 进行隐式类型转换。这对大多数程序员来说似乎相当顽固,但实际上这是 Haskell 的类型系统如此出色的原因之一。完全避免转换使很多事情变得简单得多,并且这是完全双向类型推断的先决条件。
你的情况,问题出在x / 4
。一个除法,你似乎期望结果可能是非整数。但是必须是整数,只要是整数类型1即可!所以需要先显式转换为小数类型2。最简单的方法确实是您已经发现的 fromIntegral
函数:
if isWhole $ fromIntegral x / 4 then ...
为了清楚起见:它被解析为 if isWhole ( (fromIntegral x)/4 ) then
。此时你可能想知道:如果我只是说 Haskell 从不隐式转换东西,那么为什么除以整数文字 4
就可以了?答案是 Haskell 没有整数文字!它只有各种 数字文字 。整数文字没有任何特定类型,它是多态的(即,它从上下文请求此处预期的类型,并且比该类型表现得更好——前提是类型在 Num
class).
1C 通过对结果进行整数除法截断来“解决”这个问题......这会导致大量问题,可能比你更多d 通过隐式转换得到。
2“一些小数类型”的意思是,在Haskell中,它会默认为Double
。这种类型非常快,但也不是完全没有问题,因为浮点数本质上是不精确的。我现在不确定这是否会成为您应用程序中的问题;因为 ==
比较,它很可能是。基本上,您需要指定 isWhole
的类型以防止出现这种情况,如 isWhole :: Rational -> Bool
。 Rational
类型将非整数表示为精确分数。
在 Haskell 中,/
运算符用于小数类型而不是整数类型。对于整数类型,你应该使用 div
(你可以通过用反引号包围它来使用它中缀)。您也可以使用 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
都工作准确,因此没有舍入错误的危险。