4

我有一个问题,我想了解更多,以及如何避免。我有这个代码

len :: (Num r ) => [a] -> r
len [] = 0
len xs = 1 + len ( tail xs )

avg :: (Num t) => [t] -> Double
avg xs = ( sum xs ) / ( len xs )

这会导致以下错误

len.hs:6:9:
    无法将预期类型“Double”与推断类型“t”匹配
      `t' 是一个刚性类型变量,由
          len.hs:5:12 处“avg”的类型签名
    在表达式中:(sum xs) / (len xs)
    在 `avg' 的定义中:avg xs = (sum xs) / (len xs)

现在,我知道这个错误(感谢 irc.freenode.net#haskell)是除法函数的结果

(/) :: (Fractional a) => a -> a -> a

但是,我不知道该怎么做。我的avg函数签名应该与除法运算符的怪癖(需要类型类)无关Fractional。所以,我认为克服这个问题的正确方法是通过强制转换为隐含他们类型类的类型,Fractional但我不知道如何,或者即使这是正确的?有任何想法吗?

4

4 回答 4

7

我的avg函数签名应该与除法运算符的怪癖无关

这是为什么?如果你想计算一堆整数的平均值,你必须在某个时候进行除法,所以你必须将它们从整数转换为你选择的支持除法的类型。仔细查看Num类(:i Num在 ghci 中)揭示了 : 类型的一个问题avgNum没有足够的方法——基本上足以进行加法、乘法和减法。不能保证我给的数字avg可以转换为 a Double

如果你输入一个无类型函数来计算平均值,Haskell 会以最通用的类​​型作为响应:

Prelude List> :type \x -> sum x / genericLength x
\x -> sum x / genericLength x :: (Fractional a) => [a] -> a

所以这是正确的类型avg

您可能会注意到它avg [1,2,3 :: Integer]给出了类型错误。您可以通过将参数传递给toRationalor fromIntegralfirst 来解决这个问题,它们分别使用RealIntegral实例Integer


关于表达式sum [1,2,3] / len [1,2,3]:确实,字面数字 like1的类型为Num a => a,它调用fromInteger任何类型,但表达式 like1/2具有更具体Fractional a => a的类型表达式而不是打印出来。

ghci中可能会有所帮助:set -Wall,它会在为您选择默认类型时打开大量警告,提示最通用的类​​型可能不再正确。

于 2009-11-29T23:17:53.117 回答
5

您过度限制了 avg 的类型。使用更通用的版本,avg :: (Fractional a) => [a] -> a

于 2009-11-29T23:15:48.223 回答
1

嗯,真正的问题是,如果它是整数类型,你想转换为小数类型,但如果它是小数类型,你想不管它。

试试这个

fromRational ((sum xs) % (leng xs))
于 2009-11-29T23:05:33.823 回答
0

我遇到过这个问题。我设法做的最好的事情是有两个平均功能:一个用于积分,一个用于小数:

avgInt :: (Integral i, Fractional f) => [i] -> f
avgInt xs = fromIntegral (sum xs) / fromIntegral (length xs)

avgFrac :: (Fractional f) => [f] -> f
avgFrac xs = sum xs / fromIntegral (length xs)
于 2010-10-09T15:33:52.227 回答