【问题标题】:Haskell float calculation errorHaskell浮点计算错误
【发布时间】:2016-12-20 15:28:04
【问题描述】:

我正在尝试编写一个计算曲线下面积的简短函数,但我不断收到类型不匹配错误。

以下函数中的参数:lr为求值范围,ab为曲线参数。

solve :: Int -> Int -> [Int] -> [Int] -> [Double]
solve l r a b = [area]
    where eval a b p = fromIntegral . sum . map (\(ai, bi) -> ai * p ^ bi) $ zip a b
          area = foldl (\acc p -> acc + 0.001 * eval a b p) 0 range
          range = map (\a -> (fromIntegral a :: Double ) / 1000) [l*1000..r*1000]

我感到非常沮丧,因为 Haskell 中的类型系统真的不是那么直观。有人可以建议在数值计算中处理浮点数时的最佳做法吗?


综上所述,上面的代码不起作用,因为:

  • 在类型声明中a 被声明为[Int]
  • 因此Haskell推断eval也有Int类型,因为(*)有签名Num a => a -> a -> a(所以它只接受相同类型的参数)

如果我们想在不改变输入类型的情况下将这个问题中的代数曲线计算为浮点值,我们可以将a 转换为[Double]。代码如下:

solve :: Int -> Int -> [Int] -> [Int] -> [Double]
solve l r a b = [area]
    where eval p = sum . map (\(ai, bi) -> ai * p ^^ bi) $ zip af b
          area = foldl (\acc p -> acc + 0.001 * eval p) 0 range
          range = map (\x -> (fromIntegral x :: Double ) / 1000) [l*1000..r*1000]
          af = map fromIntegral a :: [Double]

我还将^ 更改为^^ 以处理负指数。

【问题讨论】:

    标签: haskell floating-point numeric


    【解决方案1】:

    要了解类型错误,您应该了解一些类型 :)。一个好的开始是注释掉类型声明以查看 GHCi 推断的内容(在这种情况下,我们的错误不会改变,但确保我们的类型声明不是问题是一个很好的一般做法)。无论如何,当我们这样做时,我们会遇到错误:

     floatingError.hs:4:47: error:
        • No instance for (Integral Double) arising from a use of ‘eval’
        • In the second argument of ‘(*)’, namely ‘eval a b p’
          In the second argument of ‘(+)’, namely ‘0.001 * eval a b p’
          In the expression: acc + 0.001 * eval a b p
    

    我们从相关数字运算符(*)(+) 的类型签名中知道它们接受相同类型的参数。这是我们错误的原因; eval 应该是一个整数类型。但是,我们已经对其应用了fromIntegral 函数。因此,如果我们删除它,我们的函数应用程序类型检查和我们的程序编译。

    接下来,我们可以检查 GHCi 为 solve 推断出的类型签名:

    solve :: (Integral b, Integral t) =>
     t -> t -> [Double] -> [b] -> [Double]
    

    因为Int 有一个类型类Integral 的实例,我们知道我们声明的签名不会与我们修改后的函数定义冲突。


    我们的最终代码:

    solve :: Int -> Int -> [Double] -> [Int] -> [Double]
    solve l r a b = [area]
      where eval a b p = sum . map (\(ai, bi) -> ai * p ^ bi) $ zip a b
            area = foldl (\acc p -> acc + 0.001 * eval a b p) 0 range
            range = map (\a -> (fromIntegral a :: Double ) / 1000) [l*1000..r*1000]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-03-12
      • 1970-01-01
      • 1970-01-01
      • 2012-03-10
      • 1970-01-01
      • 1970-01-01
      • 2012-04-29
      相关资源
      最近更新 更多