3

三角形の 3 番目の辺を計算するために、これ (私が考えていたもの) をかなり単純なコードにしました。

toRadians :: Int -> Double
toRadians d = let deg = mod d 360
              in deg/180 * pi

lawOfCosines :: Int -> Int -> Int -> Double
lawOfCosines a b gamma = sqrt $ a*a + b*b - 2*a*b*(cos (toRadians gamma))

しかし、それを GHCi にロードしようとすると、次のエラーが発生しました。

[1 of 1] Compiling Main             ( law_of_cosines.hs, interpreted )

law_of_cosines.hs:3:18:
    Couldn't match expected type `Double' with actual type `Int'
    In the first argument of `(/)', namely `deg'
    In the first argument of `(*)', namely `deg / 180'
    In the expression: deg / 180 * pi

law_of_cosines.hs:6:26:
    No instance for (Floating Int)
      arising from a use of `sqrt'
    Possible fix: add an instance declaration for (Floating Int)
    In the expression: sqrt
    In the expression:
      sqrt $ a * a + b * b - 2 * a * b * (cos (toRadians gamma))
    In an equation for `lawOfCosines':
        lawOfCosines a b gamma
          = sqrt $ a * a + b * b - 2 * a * b * (cos (toRadians gamma))

law_of_cosines.hs:6:57:
    Couldn't match expected type `Int' with actual type `Double'
    In the return type of a call of `toRadians'
    In the first argument of `cos', namely `(toRadians gamma)'
    In the second argument of `(*)', namely `(cos (toRadians gamma))'

修正は、私の型署名を削除することであることが判明しました。これにより、問題なく動作しました。

toRadians d = let deg = mod d 360
              in deg/180 * pi

lawOfCosines a b gamma = sqrt $ a*a + b*b - 2*a*b*(cos (toRadians gamma))

and のタイプを照会すると、次のようにtoRadiansなりlawOfCosinesます。

*Main> :t toRadians
toRadians :: (Floating a, Integral a) => a -> a
*Main> :t lawOfCosines
lawOfCosines :: (Floating a, Integral a) => a -> a -> a -> a
*Main>

誰かがここで何が起こっているのか説明できますか? 私が書いた「直感的な」型シグネチャが実際に間違っていたのはなぜですか?

4

3 に答える 3

11

問題は次のtoRadiansとおりです:modタイプIntegral a => a -> a -> aがあり、したがって、degタイプがありますIntegral i => i(つまり、またはのいずれIntInteger)。

次に、を試して使用/しますがdeg/整数を取りません(積分をで除算しますdiv)。

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

解決策は、単に使用することfromIntegral :: (Integral a, Num b) => a -> bです:

toRadians :: Int -> Double
toRadians d = let deg = mod d 360
              in (fromIntegral deg)/180 * pi
于 2013-03-21T19:13:39.663 に答える
5

これらのクラスは相互に排他的であると想定されているため、型シグネチャで と を一緒に見るFloating aIntegral a、常に内部警鐘が鳴らされます。少なくとも、両方のクラスのインスタンスである標準の数値型は存在しません。GHCiは私に教えてくれます(他の多くのものと一緒に):

> :info Integral
...
instance Integral Integer -- Defined in `GHC.Real'
instance Integral Int -- Defined in `GHC.Real'
> :info Floating
...
instance Floating Float -- Defined in `GHC.Float'
instance Floating Double -- Defined in `GHC.Float'

これらのクラスが相互に排他的である理由を確認するために、両方のクラスのメソッドのいくつかを見てみましょう (これは少し手抜きになります)。fromIntegerinは、精度を落とさずに数値をにIntegral変換します。ある意味で、数学的整数 (のサブセット) であるという本質を捉えています。IntegralIntegerIntegral

一方、やFloatingなどのメソッドは、「実数」の風味が際立っています。piexp

との両方である型があったFloating場合、3.14159 に等しい整数をIntegral記述して持つことができますtoInteger pi... - そしてそれは不可能です :-)


つまり、すべての型シグネチャを;Doubleの代わりに使用するように変更する必要があります。Int結局のところ、すべての三角形が整数の辺や角度を持つわけではありません。

なんらかの理由でそれが絶対に必要ない場合は、辺 (ab引数) をlawOfCosinesに変換する必要もありますDouble。それは可能です

lawOfCosines aInt bInt gamma = sqrt $ a*a + b*b - 2*a*b*(cos (toRadians gamma)) where
    a = fromInteger aInt
    b = fromInteger bInt
于 2013-03-21T20:28:48.327 に答える
4

の型シグネチャtoRadiansは、 を受け取りますIntが、 を返しますDouble。一部のプログラミング言語では、ある言語から別の言語への変換 (ただし、元には戻らない) が自動的に行われます。Haskell はそのような言語ではありません。を使用して、手動で変換を要求する必要がありますfromIntegral

表示されているエラーはすべて、 で機能しないさまざまな操作Int、または に追加しようとしたことIntなどに起因してDoubleいます。(例: では/機能しないInt、 ではpi機能しないInt、 ではsqrt機能しないInt...)

于 2013-03-21T20:16:25.460 に答える