web-dev-qa-db-ja.com

Haskellでのべき乗

Haskell Preludeがべき乗のために2つの別個の関数を定義している理由を誰か教えてもらえますか(つまり、^および**)?型システムは、この種の重複を排除するはずだと思いました。

Prelude> 2^2
4
Prelude> 4**0.5
2.0
82
skytreebird

実際には、(^)(^^)(**)の3つの累乗演算子があります。 ^は非負の整数べき乗、^^は整数べき乗、**は浮動小数点べき乗です。

(^) :: (Num a, Integral b) => a -> b -> a
(^^) :: (Fractional a, Integral b) => a -> b -> a
(**) :: Floating a => a -> a -> a

その理由は、タイプセーフです。数値演算の結果は、通常、入力引数と同じタイプです。しかし、Intを浮動小数点の累乗にして、Int型の結果を取得することはできません。したがって、型システムにより、これを行うことができません。(1::Int) ** 0.5は型エラーを生成します。 (1::Int) ^^ (-1)についても同じことが言えます。

これを置く別の方法:Num型は^の下で閉じられます(それらは乗法逆数を持つ必要はありません)、Fractional型は^^の下で閉じられ、Floating型は**の下で閉じられます。 FractionalにはIntインスタンスがないため、負の累乗に上げることはできません。

理想的には、^の2番目の引数は、静的に負でない値に制約されます(現在、1 ^ (-2)はランタイム例外をスローします)。ただし、Preludeには自然数の型はありません。

121

Haskellの型システムは、3つのべき乗演算子を1つとして表現するほど強力ではありません。本当に欲しいのは次のようなものです:

class Exp a b where (^) :: a -> b -> a
instance (Num a,        Integral b) => Exp a b where ... -- current ^
instance (Fractional a, Integral b) => Exp a b where ... -- current ^^
instance (Floating a,   Floating b) => Exp a b where ... -- current **

インスタンスの選択はHaskellが現在許可しているよりも賢くする必要があるため、マルチパラメータータイプのクラス拡張をオンにしても、これは実際には機能しません。

28
augustss

2つの演算子を定義するのではなく、3つの演算子を定義します!レポートから:

2つの引数の累乗演算が3つあります。(^)は任意の数を非負の整数乗に、(^^)は小数を任意の整数乗に、そして(**)は2つの浮動小数点引数。 x^0またはx^^0の値は、ゼロを含むxに対して1です。 0**yは未定義です。

つまり、3つの異なるアルゴリズムがあり、そのうち2つは正確な結果(^^^)を提供し、**はおおよその結果を提供します。使用する演算子を選択することにより、呼び出すアルゴリズムを選択します。

10
Gabe

_^_では、2番目の引数がIntegralである必要があります。私が間違っていなければ、整数指数で作業していることがわかっている場合、実装はより効率的になります。また、2 ^ (1.234)のようなものが必要な場合は、ベースが整数の2であっても、結果は明らかに分数になります。より多くのオプションがあるため、どのタイプが指数関数に出入りするかをより厳密に制御できます。

Haskellの型システムには、C、Python、LISPなどの他の型システムと同じ目標はありません。アヒルのタイピングは、(ほぼ)Haskellの考え方の反対です。

4
Dan Burton