web-dev-qa-db-ja.com

Haskell、関数内でIntとFloatを乗算

Ghciで私が入力できるのはなぜですか?

5.0 * (3 - 1)
> 10.0

しかし、.hsファイルに関数を作成してロードすると、次のようになります。

test :: Float -> Int -> Int -> Float
test a b c = a * (b - c)

エラーが発生しましたか? 「予想される型 'Float'と推定される型 'Int'を一致させることができませんでした。1つの浮動小数点と2つの整数の引数を取り、上記の操作を実行する関数をどのように記述できますか?

私はそれが違いを生むならghci v6.12.1を使用しています...

24
Sam Coulter

数値リテラル(Haskellコードで数値を入力するだけ)は、固定型ではありません。それらは多態性です。それらは、具体的な型を持つことを必要とするいくつかのコンテキストで評価される必要があります。

したがって、式5.0 * (3 - 1)notIntFloatを乗算したものです。 _5.0_はいくつかのFractionalタイプである必要があり、_3_および_1_はそれぞれいくつかのNumタイプです。 _3 - 1_は、3と1の両方がsameNumタイプである必要があることを意味しますが、特定の1つに関する制約は(まだ)まだありません。そうです。減算の結果は同じ型です。

_*_は、両方の引数が同じ型でなければならず、結果も同じ型になることを意味します。 _5.0_はFractional型なので、_(3 - 1)_もそうである必要があります。 _3_、_1_、および_(3 - 1)_はいくつかのNumタイプでなければならないことはすでに知っていましたが、すべてのFractionalタイプもNumですタイプなので、この要件は競合しません。

最終結果は、式5.0 * (3 - 1)Fractionalである型であり、_5.0_、_3_、および_1_がすべて同じタイプ。 GHCiで_:t_コマンドを使用すると、これを確認できます。

_Prelude> :t 5.0 * (3 - 1)
5.0 * (3 - 1) :: Fractional a => a
_

しかし、実際にevaluateの式を使用するには、いくつかの具象型に対してそうする必要があります。これを評価して、FloatDouble、またはその他の特定のFractional型を必要とする関数に渡すと、Haskellはその型を選択します。特定の型である必要がある他のコンテキストなしで式を評価する場合、Haskellにはいくつかのデフォルトのルールがあり、自動的に1つを選択します(デフォルトのルールが適用されない場合は、あいまいな型の変数に関する型エラーが発生します)。

_Prelude> 5.0 * (3 - 1)
10.0
Prelude> :t it
it :: Double
_

上記では5.0 * (3 - 1)を評価してから、GHCiが常に評価した最後の値にバインドするmagic it変数のタイプを尋ねました。これは、式の値が_Fractional a => a_であることを計算するために、GHCiがデフォルトの_10.0_タイプをDoubleにデフォルト設定したことを示しています。その評価を行う際には、Doublesを乗算(および減算)するだけで、DoubleIntを乗算することはありません。


さて、複数の数値literalsを試してみると、タイプが異なる可能性があります。しかし、あなたのtest関数はリテラルを乗算しているのではなく、特定の既知の型の変数を乗算しています。 Haskellでは、IntFloatを乗算することはできません。これは、_*_演算子の型が_Num a => a -> a -> a_であるため、同じ数値型の2つの値を受け取り、そのタイプの結果。 IntIntを乗算してIntを取得するか、FloatFloatを乗算してFloatIntFloatを乗算して_???_を取得することはできません。

他の言語は、状況によっては、変換関数の呼び出しを暗黙的に挿入することによってのみ、この種の操作をサポートします。 Haskell neverは型間で暗黙的に変換しますが、変換関数があります。それらを呼び出したい場合は、明示的に呼び出す必要があります。これはトリックを行います:

_test :: Float -> Int -> Int -> Float
test a b c = a * fromIntegral (b - c)
_
39
Ben

代わりに以下を試してください。

_test :: Float -> Int -> Int -> Float
test a b c = a * fromIntegral (b - c)
_

なぜこれが機能するのですか?

  1. bcはどちらもIntsであるため、式_(b - c)_もIntです。
  2. _(*)_の型シグネチャは_Num a => a -> a -> a_です。
  3. aの型はFloatであるため、Haskellは_(a*)_の型シグネチャを_Float -> Float_に更新します。
  4. ただし、_(b - c)_はIntではなくFloatであるため、a * (b - c)を実行しようとするとHaskellは文句を言うでしょう。
  5. fromIntegralの型シグネチャは_(Integral a, Num b) => a -> b_です。
  6. したがって、fromIntegral (b - c)の型シグネチャは_Num b => b_です。
  7. Floatはtypeclass Numのインスタンスなので、_(*)_の型シグネチャは_Num a => a -> a -> a_であるため、a * fromIntegral (b - c)を実行できます。
  8. testの型シグネチャからの結果は、Floatと評価されます。

これがお役に立てば幸いです。

4
Aadit M Shah

署名を追加する前のtest関数はより一般的でした。

> let test a b c = a * (b - c)
> :t test
  test :: Num a => a -> a -> a -> a

あなたはそれを制限することができますが、すべてのタイプは同じでなければなりません:

test :: Fractional a => a -> a -> a -> a   -- some real types
test :: Integral   a => a -> a -> a -> a   -- all integer types
test :: Float -> Float -> Float -> Float  
test :: Int   -> Int   -> Int   -> Int   

test :: Int   -> Float -> Float -> Float  --wrong

ところで、2Intではなく、0.2Floatではありません。gchiに質問してみましょう。

> :t 2
 2 :: Num a => a
> :t 0.2
 0.2 :: Fractional a => a
3
viorior

浮動小数点数を乗算する前に、整数にfromIntegralを使用する必要があります。

http://www.haskell.org/haskellwiki/Converting_numbers

GHCIでは、使用するまで数値は浮動小数点数または整数とは見なされません。 (例:実行時)。これは、REPLスタイルの開発に適しています。

コンパイラ本体では、 自動強制はありません。それそれ 乗算は、2つの値が乗算をサポートする型クラスに属している必要があることを前提としています。例:intの乗算、またはfloatの乗算。他の明示的に型指定された関数を使用しなかったため、intを想定しました。その仮定は(オプションの)型シグネチャによって異なります。

3
David-SkyMesh