haskell分数Int的实例,用于定义平均值

时间:2018-03-11 15:29:47

标签: haskell

我在编写平均函数时出现此错误,如下所示

module Assignment where

average xs = sum xs / length xs

它到底想要什么? 是要求定义吗?我也提供了相同的错误

1 个答案:

答案 0 :(得分:2)

你给出了一个定义,这不是问题所在。问题是Haskell无法在两个/上应用Int运算符,确实:

Prelude> (2 :: Int) / 3

<interactive>:1:1: error:
    • No instance for (Fractional Int) arising from a use of ‘/’
    • In the expression: (2 :: Int) / 3
      In an equation for ‘it’: it = (2 :: Int) / 3

在终端中,我们要将23分开。我们强迫Haskell对Int使用2,并抱怨同样的事情。

原因是(/)中定义了Fractional

class Num a => Fractional a where
  (/) :: a -> a -> a
  recip :: a -> a
  fromRational :: Rational -> a
  {-# MINIMAL fromRational, (recip | (/)) #-}

如您所见,该定义指出所有Fractional都是Num s,但并非所有Num都是Fractional s。这是合乎逻辑的。注意(/) :: a -> a -> a的签名。如果我们在Int s上使用它,它将具有签名(/) :: Int -> Int -> Int。但这意味着2/301(假设我们获得最接近的结果)。但是很开心。由于人们会因此而犯很多错误,Haskell库的设计者决定使Int成为Fractional,但提供了div函数积分划分。

当然这对我们没什么帮助。因为我们可能不希望结果首先是Int。所以,我们可以做的是将length结果转换Fractional。例如,我们可以使用fromIntegral :: (Integral a, Num b) => a -> b函数执行此操作。因此我们可以将函数重新定义为:

average :: Fractional a => [a] -> a
average xs = sum xs / fromIntegral (length xs)

请注意,此处列表的元素的类型决定了平均输出的类型。该类型也需要Fractional。所以我们不能处理Int的列表,除非我们首先预处理列表(例如调用fromIntegral到列表的元素)。如果列表包含整数元素,我们可以先计算总和,然后将其转换为Fractional(从那时起结果会更精确)。然后我们可以将它定义为:

integrals_average :: (Integral a, Fractional b) => [a] -> b
integrals_average xs = fromIntegral (sum xs) / fromIntegral (length xs)