Haskelltypes令一个简单的“平均”函数感到沮丧

我正在玩初学Haskell,我想写一个平均function。 这似乎是世界上最简单的事情,对吗?

错误。

看起来像Haskell的types系统禁止平均工作在一个通用的数字types – 我可以得到它在一个积分列表,或一个分数列表,但不是两个。

我想要:

average :: (Num a, Fractional b) => [a] -> b average xs = ... 

但我只能得到:

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

要么

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

而第二个似乎工作。 直到我尝试传递一个variables。

 *Main> averageFrac [1,2,3] 2.0 *Main> let x = [1,2,3] *Main> :tx x :: [Integer] *Main> averageFrac x <interactive>:1:0: No instance for (Fractional Integer) arising from a use of `averageFrac ' at <interactive>:1:0-8 Possible fix: add an instance declaration for (Fractional Integer) In the expression: average x In the definition of `it': it = averageFrac x 

显然,Haskell对它的types真的很挑剔。 这就说得通了。 但是,当他们都可以[数字]

我是否错过了RealFrac的一个明显的应用?

有没有办法强制积分成小数分数不会呛,当它得到一个分数input?

有什么方法可以使用Eithereither可以使某种多态的平均函数,可以在任何types的数组数组?

Haskell的types系统是否完全禁止这个函数存在?

学习Haskell就像学习微积分。 这是非常复杂的,基于理论的高峰,有时候这个问题非常复杂,甚至不足以正确地提出问题,所以任何见解都会被热烈接受。

(另外,脚注:这是基于一个作业问题。每个人都同意averageFrac,上面得到满分,但我有一个偷偷摸摸的怀疑,有一种方法,使其在积分和分数arrays上工作)

所以从根本上说,你受(/)types的限制:

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

顺便说一句,你也想Data.List.genericLength

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

那么如何去除更一般的东西呢?

 import Data.List average xs = realToFrac (sum xs) / genericLength xs 

只有一个真正的约束(诠释,整数,浮法,双)…

 average :: (Real a, Fractional b) => [a] -> b 

所以这会把任何真正的任何分数。

注意到所有海报都被Haskell中的多态数字文字所捕获。 1不是一个整数,它是任何数字。

Real类只提供一种方法:将类Num中的值转换为理性的能力。 这正是我们在这里所需要的。

因此,

 Prelude> average ([1 .. 10] :: [Double]) 5.5 Prelude> average ([1 .. 10] :: [Int]) 5.5 Prelude> average ([1 .. 10] :: [Float]) 5.5 Prelude> average ([1 .. 10] :: [Data.Word.Word8]) 5.5 

这个问题已经得到了很好的回答,我想我可以补充一些东西。

以这种方式计算平均值时:

average xs = realToFrac (sum xs) / genericLength xs

你的代码将做的是遍历列表两次,一次计算其元素的总和,一次得到它的长度。 据我所知,GHC还没有能够优化这一点,并且一次性计算总和和长度。

即使作为一个初学者思考它和可能的解决scheme,它也不会受到伤害,例如,平均函数可以使用计算总和和长度的折叠来书写; 在ghci上:

 :set -XBangPatterns import Data.List let avg l=let (t,n) = foldl' (\(!b,!c) a -> (a+b,c+1)) (0,0) l in realToFrac(t)/realToFrac(n) avg ([1,2,3,4]::[Int]) 2.5 avg ([1,2,3,4]::[Double]) 2.5 

该function看起来并不高雅,但性能更好。

更多关于Dons博客的信息:

http://donsbot.wordpress.com/2008/06/04/haskell-as-fast-as-c-working-at-a-high-altitude-for-low-level-performance/

由于dons在回答你的问题方面做得如此出色,我会努力质疑你的问题….

例如,在你的问题中,你首先在给定的列表上运行一个平均值,得到一个很好的答案。 然后, 你将看起来像完全相同的列表 ,将其分配给一个variables,然后使用函数variables…,然后爆炸。

你在这里碰到的是编译器中的一个设置,叫做DMR: D是一个同构的缩写。 当你直接将这个列表传递给函数时,编译器不会假定数字是哪一种types,它只是根据用法推断出它可能的types,然后在不能再缩小字段的情况下select一个types。 这有点像鸭子打字那样。

无论如何,当您将列表分配给一个variables时,DMR已经踢了进来。由于您已经将列表放入了一个variables中,但是没有提示如何使用它,DMR让编译器select了一种types,情况下,它select了一个匹配的forms,似乎适合: Integer 。 由于你的函数在它的/操作中不能使用Integer(它需要Fractional类中的一个types),所以非常抱怨:在Fractional类中没有Integer的实例。 你可以在GHC中设置选项,这样它就不会强制你的值变成一个单一的forms(“mono-morphic”,得到它?),直到它需要,但它使任何错误消息稍微难以弄清楚。

现在,另一张纸条上,你回答了那个引起我注意的答案:

我误导了cs.ut.ee/~varmo/MFP2004/PreludeTour.pdf最后一页上的图表,显示了浮动NOT不是从Realinheritance的属性,于是我认为它们将不共享任何types。

Haskell的types与你习惯的不一样。 RealFloating是types类,它比对象类更像接口。 他们告诉你如何使用该类中的types,但这并不意味着某种types不能做其他事情,只不过有一个接口意味着(OO风格)类不能有任何其他的。

学习Haskell就像学习微积分

我会说学习Haskell就像学习瑞典语 – 有很多简单的东西(字母,数字)看起来和工作是一样的,但也有一些看起来应该是一样的东西,当他们真的意味着什么其他。 但是一旦你stream利的话,你的老朋友会惊讶于你如何才能把这些古怪的东西吹出来,让美丽的美女做出惊人的花样。 奇怪的是,从一开始就有许多人参与了Haskell,他们也知道瑞典语。 也许这个比喻不仅仅是一个比喻…

 :m Data.List let list = [1..10] let average = div (sum list) (genericLength list) average 

是的,Haskell的types系统非常挑剔。 这里的问题是fromIntegral的types:

 Prelude> :t fromIntegral fromIntegral :: (Integral a, Num b) => a -> b 

fromIntegral将接受一个I​​ntegral而不是任何其他types的Num。 (/),另一方面只接受小数。 你怎么把这两个工作在一起?

那么,总和function是一个好的开始:

 Prelude> :t sum sum :: (Num a) => [a] -> a 

总和采取任何数量的列表,并返回一个数字。

你的下一个问题是列表的长度。 长度是一个Int:

 Prelude> :t length length :: [a] -> Int 

你需要将Int转换成Num。 这就是整体所做的。

所以现在你已经有了一个函数返回一个数字和另一个函数返回一个数字。 有一些规则可以查询数字的types提升,但基本上在这一点上,你很好走:

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

让我们试试看:

 Prelude> average [1,2,3,4,5] 3.0 Prelude> average [1.2,3.4,5.6,7.8,9.0] 5.4 Prelude> average [1.2,3,4.5,6,7.8,9] 5.25