【问题标题】:Why doesn't this average function in haskell work?为什么haskell中的这个平均功能不起作用?
【发布时间】:2012-02-13 15:06:26
【问题描述】:

我想在 Haskell 中做一个简单的平均(mean)函数,所以我在 ghci 中尝试了以下方法:

ghci> let avg xs = (sum xs) / (length xs)

它会抛出以下错误:

No instance for (Fractional Int)
  arising from a use of `/'
Possible fix: add an instance declaration for (Fractional Int)
In the expression: (sum xs) / (length xs)
In an equation for `avg': avg xs = (sum xs) / (length xs)

所以,我决定尝试以下方法来分解它:

ghci> let a = (sum [1,2])
ghci> let b = (length [1,2])

一切都很好。

然后我尝试了以下

ghci> a/b

我收到以下错误:

Couldn't match expected type `Integer' with actual type `Int'
In the second argument of `(/)', namely `b'
In the expression: a / b
In an equation for `it': it = a / b

那么,在 Haskell 中 IntegerInt 不同吗? - 如果是这样,我怎样才能使原来的功能工作?

【问题讨论】:

  • 需要更多 fromIntegral。 =)

标签: haskell types casting


【解决方案1】:

问题是

length :: [a] -> Int

但是

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

所以,当您说whatever / length xs 时,它不输入​​,因为 Int 不是小数类型!这就是“没有实例...”错误试图告诉您的内容。这个定义会起作用:

GHCi> let avg xs = sum xs / fromIntegral (length xs)

在这里,我们使用fromIntegral :: (Integral a, Num b) => a -> b 将我们从length 获得的 Int 转换为小数。请注意,由此产生的函数仅适用于小数列表(但例如 avg [1,2,3] 仍然可以正常工作)。

解释一下“分段”执行时遇到的错误,这是因为在let a = sum [1,2] 中,列表的元素是整数,所以它们的总和是整数,但在let b = length [1,2] 中,结果长度是整数,根据我上面显示的length 的类型。因此,当您执行 a/b 时,它甚至在意识到 Int 和 Integer 不是 Fractional 的实例之前就失败了——因为 (/) 接受两个相同类型的参数,所以它不可能工作。

是的,Integer 和 Int 是不同的——Int 是固定精度的整数类型,通常是数字字的大小,如 C 中的 long,而 Integer 是任意精度的 bignum,能够存储任何整数;或者至少,任何适合您 RAM 的整数 :)

另一种可能的方法是将avg xs 定义为sum xs / genericLength xs,使用Data.List.genericLength,它适用于任何数字类型,而不仅仅是Ints:

genericLength :: (Num b) => [a] -> b

为此,您必须在 GHCi 中 import Data.List。另一种可能的方法(但会导致完全不同的函数)是使用整数除法:let avg xs = sum xs `div` length xs(注意:a `div` b 只是 div a b;这种语法糖适用于每个函数)。

【讨论】:

  • 由于您使用参数定义avg,即let avg xs = ...,因此单态限制不适用,并且不进行默认设置。实际上,avg 的类型定义为(Fractional a) => [a] -> a。但是,如果您尝试使用它,则该类型默认为 Doubleavg [1.5, 2].
猜你喜欢
  • 1970-01-01
  • 2016-07-02
  • 2018-11-04
  • 1970-01-01
  • 2019-10-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多