首页 > 解决方案 > Haskell Fractional 无法推断 (RealFrac a) 由于使用“round”而产生

问题描述

我不想执行整数除法。我想采用小数输入类型,对其进行除法,将结果四舍五入为整数并返回。

piCalc :: (Fractional a, Ord a, Integral b) => a -> (a, b)
piCalc z = (piCalc' steps 1 0, steps)
    where steps = round $ (4-z)/(2*z) + 1

piCalc' :: (Integral a, Ord a, Fractional b) => a -> a -> a -> b
piCalc' 0 s k = 0
piCalc' steps s k = (fromInteger s*4)/(fromInteger 2*k + 1) + piCalc' (steps-1) ((-1)*s) (k+1)

main = do
    print (piCalc 0.001)

我收到以下错误:

Recursion.hs:59:19: error:
    * Could not deduce (RealFrac a) arising from a use of `round'
      from the context: (Fractional a, Ord a, Integral b)
        bound by the type signature for:
                   piCalc :: forall a b.
                             (Fractional a, Ord a, Integral b) =>
                             a -> (a, b)
        at Recursion.hs:57:1-58
      Possible fix:
        add (RealFrac a) to the context of
          the type signature for:
            piCalc :: forall a b.
                      (Fractional a, Ord a, Integral b) =>
                      a -> (a, b)
    * In the first argument of `($)', namely `round'
      In the expression: round $ (4 - z) / (2 * z) + 1
      In an equation for `steps': steps = round $ (4 - z) / (2 * z) + 1
   |
59 |     where steps = round $ (4-z)/(2*z) + 1
   |                   ^^^^^

Recursion.hs:63:34: error:
    * Couldn't match expected type `Integer' with actual type `a'
      `a' is a rigid type variable bound by
        the type signature for:
          piCalc' :: forall a b.
                     (Integral a, Ord a, Fractional b) =>
                     a -> a -> a -> b
        at Recursion.hs:61:1-64
    * In the first argument of `fromInteger', namely `s'
      In the first argument of `(*)', namely `fromInteger s'
      In the first argument of `(/)', namely `(fromInteger s * 4)'
    * Relevant bindings include
        k :: a (bound at Recursion.hs:63:17)
        s :: a (bound at Recursion.hs:63:15)
        steps :: a (bound at Recursion.hs:63:9)
        piCalc' :: a -> a -> a -> b (bound at Recursion.hs:62:1)
   |
63 | piCalc' steps s k = (fromInteger s*4)/(fromInteger 2*k + 1) + piCalc' (steps-1) ((-1)*s) (k+1)
   |                                  ^^^

在 piCalc(顶部函数)中将 Fractional 更改为 RealFrac 为我修复了第一个错误,但我不明白为什么或不知道这是否是一个“好”的解决方案。在谷歌搜索时,我几乎找不到任何 RealFrac 的使用示例。我不知道是什么导致了第二个错误。

标签: haskell

解决方案


约束RealFrac意味着Real和。意味着数字必须是一维的(与二维复数或更高维向量相反)。需要,因为它必须将其输入映射到一维整数集上。并且显然需要,否则舍入没有多大意义。所以,需要一个作为输入。更改为是第一个错误的正确解决方案。FractionalRealroundRealroundFractionalroundRealFracFractionalRealFrac

第二个错误是因为您指定您的类型是Integral,但随后您应用了fromInteger需要具体的函数Integer。您应该更改fromIntegerfromIntegral.

然后你仍然会得到一个错误,因为你写了fromInteger 2*k,这意味着(fromInteger 2) * k在 Haskell 中,但你可能是指fromInteger (2 * k)or 2 * fromInteger k。这也应该改为fromIntegral.

此代码编译没有错误:

piCalc :: (RealFrac a, Ord a, Integral b) => a -> (a, b)
piCalc z = (piCalc' steps 1 0, steps)
    where steps = round $ (4-z)/(2*z) + 1

piCalc' :: (Integral a, Ord a, Fractional b) => a -> a -> a -> b
piCalc' 0 s k = 0
piCalc' steps s k = (fromIntegral s*4)/(2 * fromIntegral k + 1) + piCalc' (steps-1) ((-1)*s) (k+1)

main = do
    print (piCalc 0.001)

如果您只是对计算 pi 感兴趣,那么选择更具体的类型可能更有意义,例如DoubleorRational代替RealFrac a => aandIntInteger代替Integral a => a. 您可以保持实现相同,只更改签名:

piCalc :: Double -> (Double, Int)
piCalc z = (piCalc' steps 1 0, steps)
    where steps = round $ (4-z)/(2*z) + 1

piCalc' :: Int -> Int -> Int -> Double
piCalc' 0 s k = 0
piCalc' steps s k = (fromIntegral s*4)/(2 * fromIntegral k + 1) + piCalc' (steps-1) ((-1)*s) (k+1)

main = do
    print (piCalc 0.001)

推荐阅读