简体   繁体   English

如何处理Haskell中的类型

[英]How to deal with types in Haskell

I've started learning Haskell and I've been reading "Learn You a Haskell for Great Good". 我已经开始学习Haskell,并且一直在阅读“学习Haskell带来的好处”。 I read up to halfway through the Modules chapter. 我阅读了“模块”一章的一半内容。 A friend showed me codewars and I decided to put some of what I've learnt to the test. 一位朋友向我展示了代码战,我决定将我学到的一些东西进行测试。

I am trying to make a function which returns a Boolean of whether the given Integral is a power of 4. Here is the code. 我正在尝试制作一个函数,该函数返回一个布尔值,该布尔值是否为给定的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

This is the error message I get. 这是我收到的错误消息。

/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

What am I doing wrong? 我究竟做错了什么?

I used fromIntegral in isWhole to fix a similar error and isWhole now works fine individually. 我在isWhole中使用了fromIntegral来修复类似的错误,并且isWhole现在可以单独正常使用了。 However, I can't seem to get rid of these errors in isPowerOf4. 但是,我似乎无法摆脱isPowerOf4中的这些错误。 I've tried the possible fixes GHC provides, but I probably wasn't doing it right. 我已经尝试了GHC提供的可能的修复程序,但是我可能做得不好。

I'd rather keep the type signature of the isPowerOf4 function, as that is provided by codewars, so I'm guessing it's a requirement. 我宁愿保留isPowerOf4函数的类型签名,因为这是代码战提供的,所以我猜想这是必需的。

In Haskell the / operator is for fractional types not integral ones. 在Haskell中, /运算符用于分数类型,而不是整数类型。 For integral types you should use div (you can use it infix by surrounding it in backticks). 对于整数类型,应使用div (您可以通过将其括在反引号中来使用infix)。 You can also use mod or rem to find the remainder, like % in typical languages (they are different for negative numbers) 您还可以使用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

As @leftaroundabout explained / is floating point division and the results may be object to rounding error. 正如@leftaroundabout所解释的/是浮点除法,结果可能是舍入误差的对象。 Therefore your is isWhole function is not guarantied to work correctly in all cases. 因此,不能保证您的isWhole函数在所有情况下都能正常工作。

A better approach would be to use integer division and modulo, as suggested by @Jubobs. 更好的方法是使用@Jubobs建议的整数除法和取模。

div is integer division it gives you the result of a division with out the remainder. div是整数除法,它为您提供除法结果,而无余数。 That's the same as you expect from truncate (x / 4) . 这与您对truncate (x / 4)期望相同。 This should be rewritten as div x 4 , so it's not relying on floating point numbers. 应将其重写为div x 4 ,因此它不依赖浮点数。

For isWhole you can use modulo this is the rest of an integer division. 对于isWhole您可以使用模,这是整数除法的其余部分。 (eg mod 5 4 == 1 ) If it is zero there is no remainder, the result is a whole number. (例如mod 5 4 == 1 )如果为零,则没有余数,结果为整数。 Instead of isWhole (x / 4) you can use mod x 4 == 0 . 可以使用mod x 4 == 0代替isWhole (x / 4) Both div and mod work exact so there is not danger of rounding errors. divmod可以精确工作,因此没有舍入错误的危险。

Haskell never, ever does implict type conversions. 哈斯克尔永远, 永远不会隐式类型转换。 That may seem rather obdurate to most programmers, but actually it is one of the reasons why Haskell's type system works so great. 对于大多数程序员来说,这似乎有些固执,但这实际上是Haskell的类型系统如此出色的原因之一。 Completely shunning conversions makes many things much simpler, and it's pretty prerequisite for full bidirectional type inference. 完全回避的转换使许多事情变得更加简单,这是完全双向类型推断的前提。

In your case, the problem is in x / 4 . 在您的情况下,问题出在x / 4 A division, and you seem expect the result to be possibly noninteger. 除法,您似乎希望结果可能不是整数。 But it must be integer, as long as it has an integral type 1 ! 但是它必须是整数,只要它具有整数类型1即可 So you need to explicitly convert to a fractional type 2 first. 因此,您需要先明确转换为分数类型2 The simplest way is indeed the fromIntegral function you've already discovered: 实际上,最简单的方法是您已经发现的fromIntegral函数:

   if isWhole $ fromIntegral x / 4 then ...

To make it clear: that's parsed as if isWhole ( (fromIntegral x)/4 ) then . 明确说明:这就像if isWhole ( (fromIntegral x)/4 ) then解析一样。 At this point you may wonder: if I just said Haskell never implicitly converts stuff, then why is it fine to divide by the integer literal 4 ? 在这一点上,您可能会怀疑:如果我只是说过Haskell从来没有隐式地转换东西,那为什么用整数4来除法还可以呢? The answer to that is, Haskell doesn't have integer literals! 答案是,Haskell没有整数文字! It only has various numeric literals . 它只有各种数字文字 A whole-number literal doesn't have any particular type, it is polymorphic (ie, it requests from the context what type is expected here , and than behaves as that type – provided that the type is in the Num class). 整数文字没有任何特定的类型,它是多态的(即,它从上下文中请求此处应使用哪种类型 ,并且如果该类型位于Num类中,则该行为类似于该类型)。


1 C “solves” this problem by making integer division trucate the result... which leads to a load of problems, probably more than you'd get with implicit conversion. 1 C通过使整数除以trutru结果来“解决”该问题……这导致了很多问题,可能比隐式转换要多。

2 “Some fractional type” means, in Haskell, it'll default to Double . 2 “某些分数类型”在Haskell中表示默认为Double That type is very fast, but it's also not entirely unproblematic because floating-point numbers are inherently inexact. 这种类型非常快,但也不是完全没有问题,因为浮点数本质上是不精确的。 I'm not sure right now if this would be a problem in your application; 我现在不确定这是否会影响您的应用程序; because of the == comparison it might well be. 由于==比较,它很可能是。 Basically, you need to specify the type of isWhole to prevent that, as isWhole :: Rational -> Bool . 基本上,您需要指定isWhole的类型来防止这种情况,例如isWhole :: Rational -> Bool The Rational type represents noninteger numbers as exact fractions. Rational类型将非整数表示为精确分数。

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

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