web-dev-qa-db-ja.com

Haskellの型は単純な「平均」関数をイライラさせます

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の学習は、微積分の学習に似ています。それは本当に複雑で、理論の山に基づいており、時には問題が非常に複雑で、質問を正確に表現するのに十分なことさえ知らないので、洞察は温かく受け入れられます。

(また、脚注:これは宿題の問題に基づいています。誰もが、上記のaverageFracが完全なポイントを獲得することに同意しますが、IntegralおよびFractionalの両方のアレイで動作させる方法があるのではないかと疑っています)

67
jakebman

基本的に、(/)のタイプに制約されます。

(/) :: (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

そのため、任意のRealを任意のFractionalに取り込みます。

そして、Haskellの多態性数値リテラルに捕らえられるすべてのポスターに注意してください。 1は整数ではなく、任意の数値です。

Realクラスは、クラスNumの値を有理数に変換する機能という1つのメソッドのみを提供します。ここでまさにそれが必要です。

したがって、

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
94
Don Stewart

質問はドンによって非常によく答えられました、私は何かを加えるかもしれないと思いました。

この方法で平均を計算する場合:

average xs = realToFrac (sum xs) / genericLength xs

コードは、リストを2回走査して、1回は要素の合計を計算し、1回はその長さを取得します。私の知る限り、GHCはまだこれを最適化して、1回のパスで合計と長さの両方を計算することはできません。

初心者でも考えられる解決策を考えても害はありません。たとえば、平均関数は合計と長さの両方を計算するフォールドを使用して記述できます。 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

この関数はエレガントではありませんが、パフォーマンスは向上しています。

Donsブログの詳細:

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

25
David V.

Donsはあなたの質問に答えるのに非常に良い仕事をしたので、私はあなたの質問の質問に取り組みます。

たとえば、特定のリストで最初に平均を実行し、適切な答えを得る質問で。次に、まったく同じリストのように見えるものを取得します、変数に割り当ててから、変数...という関数を使用します。

ここで実行したのは、DMRと呼ばれるコンパイラのセットアップです。[〜#〜] d [〜#〜] readed [〜#〜] m [〜#〜]擬態[〜#〜] r [〜#〜]制限。リストを関数に直接渡したとき、コンパイラーは、数値がどのタイプであるかを仮定せず、使用法に基づいてどのタイプであるかを推測し、フィールドをそれ以上絞り込むことができなくなったら、それを選択しました。アヒルのタイピングの正反対のようなものです。

とにかく、リストを変数に割り当てたとき、DMRが作動しました。リストを変数に入れたが、それをどのように使用するかについてのヒントが与えられなかったため、DMRはコンパイラーに、ケースでは、フォームに一致し、適合すると思われるものを選択しました:Integer。関数は/操作で整数を使用できなかったため(Fractionalクラスの型が必要です)、非常に不満があります。IntegerのインスタンスはありませんFractionalクラス。 GHCで設定できるオプションがあるので、必要になるまで値を単一の形式(「単相」、取得?)に強制しませんが、エラーメッセージを理解するのは少し難しくなります。

さて、別のメモで、あなたは私の目を引いたドンの答えに返信がありました:

Cs.ut.ee/~varmo/MFP2004/PreludeTour.pdfの最後のページにあるRealからプロパティを継承しないフローティングを示すチャートに誤解され、共通のタイプを共有しないと仮定しました。

Haskellは、あなたが慣れているものとは異なる型を行います。 RealFloatingは型クラスであり、オブジェクトクラスよりもインターフェイスのように機能します。彼らはあなたがそのクラスにあるタイプで何ができるかを教えてくれますが、あるタイプが他のことをできないという意味ではなく、1つのインターフェースを持つことは、(n OOスタイル)クラスができないことを意味します他のものがあります。

Haskellの学習は微積分の学習に似ています

Haskellを学ぶことはスウェーデン語を学ぶことに似ていると思います-同じように見えて機能する小さなシンプルなもの(文字、数字)がたくさんありますが、実際には何かを意味するときに、1つのことを意味するように見える言葉もありますそうしないと。しかし、ひとたび流getになれば、ゴージャスな美人に驚くようなトリックをさせるこの奇妙なものを吐き出す方法に、普通の友達は驚くでしょう。不思議なことに、最初からHaskellに関係している多くの人々がいます。彼らはスウェーデン語も知っています。たぶんその比phorは単なる比phor以上のものです...

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