1. ホーム
  2. haskell

[解決済み] 単純な「平均」関数に苛立つハスケル型

2023-07-21 11:43:34

質問

初心者のHaskellで遊んでいるのですが、平均関数を書きたいと思いました。 それは世界で最も簡単なことのように思えました、そうですか?

間違ってますね。

Haskellの型システムは、平均が一般的な数値型で動作することを禁じているようです。積分のリストや分数のリストで動作させることはできますが、両方は無理です。

私が欲しいのは

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)

で、2番目はうまくいくようです。 変数を渡そうとするまでは

*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]である可能性があるときは、そうではありません。

RealFracの明らかなアプリケーションを見逃していませんか?

分数の入力を受けたときに窒息しないような、積分を分数に強制する方法はありますか?

を使用する方法はありますか? Eithereither を使用して、任意の種類の数値配列で動作する多相平均関数のようなものを作成することができますか?

Haskellの型システムは、この関数が存在することを完全に禁止していますか?

Haskellを学ぶことは、微積分を学ぶようなものです。 それは本当に複雑で、山のような理論に基づいています。そして、時々、問題は、質問を正しく表現するのに十分な知識さえないほど、気が遠くなるほど複雑です。

(また、脚注: これは宿題の問題に基づいています。しかし、私は、積分と分数の両方の配列で動作するようにする方法があるのではないかと、ひそかに思っています。)

どのように解決するのですか?

根本的に、(/)の種類に制約されているんですね。

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

ちなみに、Data.List.genericLengthも必要です。

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

では、fromIntegralを削除して、より一般的なものにするのはどうでしょう。

import Data.List

average xs = realToFrac (sum xs) / genericLength xs

はReal制約(Int, Integer, Float, Double)のみを持っています...

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

これでどんなリアルもどんなフラクショナルになるわけだ。

そして、Haskellの多相な数値リテラルに引っかかるすべての投稿者に注意してください。1は整数ではありません、任意の数です。

Realクラスはたった1つのメソッドを提供します: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