Haskell 类型令人沮丧的简单“平均"函数 [英] Haskell types frustrating a simple 'average' function

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

问题描述

我在玩初学者 Haskell,我想写一个普通的函数.这似乎是世界上最简单的事情,对吧?

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?

错了.

似乎 Haskell 的类型系统禁止 average 处理通用数字类型 - 我可以让它处理整数列表或分数列表,但不能同时处理两者.

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.

我想要:

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)

第二个似乎有效.直到我尝试传递一个变量.

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

显然,Haskell 对它的类型非常挑剔.这就说得通了.但不是当他们都可以是 [Num]

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

我是否遗漏了 RealFrac 的一个明显应用?

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?

有什么方法可以使用 Eithereither 来制作某种可以在任何类型的数值数组上工作的多态平均函数?

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?

Haskell 的类型系统是否完全禁止此功能的存在?

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

学习 Haskell 就像学习微积分.这真的很复杂,而且基于大量的理论,有时问题非常复杂,我什至不知道如何正确地表达问题,所以任何见解都会被热烈接受.

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.

(另外,脚注:这是基于作业问题.每个人都同意上面的 averageFrac 得到满分,但我偷偷地怀疑有一种方法可以使它在积分和分数阵列上都能工作)

(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

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

BTW, you also want Data.List.genericLength

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

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

So how about removing the fromIntegral for something more general:

import Data.List

average xs = realToFrac (sum xs) / genericLength xs

它只有一个 Real 约束(Int、Integer、Float、Double)...

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

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

这样就可以将任何 Real 转换为任何小数.

So that'll take any Real into any Fractional.

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

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

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

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.

因此,

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天全站免登陆