Haskell类型使一个简单的“平均”函数失效 [英] Haskell types frustrating a simple 'average' function

查看:74
本文介绍了Haskell类型使一个简单的“平均”函数失效的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在玩初学Haskell,我想写一个平均功能。它看起来是世界上最简单的东西,对吧?

错误。



看起来像Haskell的类型系统禁止使用泛型数字类型的平均值 - 我可以让它在积分列表或者Fractionals列表上工作,但不能同时使用。



我想要:

  average ::(数字a,小数b)=> [a]  - > b 
平均值xs = ...

但我只能得到:

  averageInt ::(积分a,分数b)=> [a]  - > b 
averageInt xs = fromIntegral(sum xs)/ fromIntegral(length xs)

  averageFrac ::(Fractional a)=> [a]  - > a 
averageFrac xs =总和xs / fromIntegral(长度xs)

上班。直到我尝试传递一个变量。

  * Main> averageFrac [1,2,3] 
2.0
* Main>让x = [1,2,3]
* Main> :t x
x :: [Integer]
* Main> averageFrac x

< interactive>:1:0:
在< interactive>使用'averageFrac'时没有(小数整数)
的实例:1: 0-8
可能的修正:为(小数整数)
添加实例声明在表达式中:平均值x
在it的定义中:it = averageFrac x

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



我是否遗漏了RealFrac的一个显而易见的应用?



有没有办法强制积分成小数,它不会在收到小数输入时憋住?

有什么方法可以使用要么或者来创建某种类型的多态平均函数,它可以用于任何类型的数组数组?

< Haskell的类型系统完全禁止这个函数存在吗?



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

(另外,脚注:这是基于一个家庭作业问题,每个人都同意averageFrac在上面获得满分,但我有一个偷偷摸摸的猜测,有一种方法可以使它在Integral和Fractional数组上工作)

解决方案

基本上,您受到(/)类型的约束:

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

顺便说一句,您还需要Data.List.genericLength

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

那么如何删除fromIntegral以获取更一般的东西:

  import Data.List 

average xs = realToFrac(sum xs)/ genericLength xs

只有一个实数约束(Int,Integer,Float,Double)...

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

这样就可以将任何实数分成任何分数。



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

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



因此,

 前奏>平均值([1..10] :: [Double])
5.5
Prelude> average([1..10] :: [Int])
5.5
Prelude>平均值([1..10] :: [浮点])
5.5
前奏> ([1..10] :: [Data.Word.Word8])
5.5


I'm playing around with beginner Haskell, and I wanted to write an average function. It seemed like the simplest thing in the world, right?

Wrong.

It seems like Haskell's type system forbids average from working on a generic numeric type - I can get it to work on a list of Integrals, or an list of Fractionals, but not both.

I want:

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

But I can only get:

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

or

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

and the second one seems to work. Until I try to pass a variable.

*Main> averageFrac [1,2,3]
2.0
*Main> let x = [1,2,3]
*Main> :t x
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

Apparently, Haskell is really picky about its types. That makes sense. But not when they could both be [Num]

Am I missing an obvious application of RealFrac?

Is there way to coerce Integrals into Fractionals that doesn't choke when it gets a Fractional input?

Is there some way to use Either and either to make some sort of polymorphic average function that would work on any sort of numeric array?

Does Haskell's type system outright forbid this function from ever existing?

Learning Haskell is like learning Calculus. It's really complicated and based on mountains of theory, and sometimes the problem is so mindbogglingly complex that I don't even know enough to phrase the question correctly, so any insight will be warmly accepted.

(Also, footnote: this is based off a homework problem. Everybody agrees that averageFrac, above, gets full points, but I have a sneaking suspicion that there is a way to make it work on both Integral AND Fractional arrays)

解决方案

So fundamentally, you're constrained by the type of (/):

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

BTW, you also want Data.List.genericLength

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

So how about removing the fromIntegral for something more general:

import Data.List

average xs = realToFrac (sum xs) / genericLength xs

which has only a Real constraint (Int, Integer, Float, Double)...

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

So that'll take any Real into any Fractional.

And note all the posters getting caught by the polymorphic numeric literals in Haskell. 1 is not an integer, it is any number.

The Real class provides only one method: the ability to turn a value in class Num to a rational. Which is exactly what we need here.

And thus,

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

这篇关于Haskell类型使一个简单的“平均”函数失效的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆