我正在尝试编写一个计算曲线下面积的短函数,但我一直遇到类型不匹配错误。
以下函数中的参数:l
,r
是评估范围,a
和b
是曲线的参数。
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]
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]
我还将^
更改为^^
以处理否定指数。
答案 0 :(得分:4)
要了解类型错误,您应该浏览一些类型:)。一个好的开始是通过注释类型声明来查看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]