16

私は小さな Haskell プログラムを持っていますが、それを実行するとなぜゼロ除算の例外がスローされるのか知りたいです (GHC 7.0.3)

import qualified Data.ByteString.Lazy as B
import Codec.Utils

convert :: B.ByteString -> [Octet]
convert bs = map (head . toTwosComp) $ B.unpack bs

main = putStrLn $ show $ convert $ B.pack [1, 2, 3, 4]

ここで何が起こっているのかを理解してくれる人はいますか?

4

2 に答える 2

17

これを次のように減らすことができます。

GHCi> toTwosComp (1 :: Word8)
*** Exception: divide by zero

これは、Word16、Int、Integer、または任意の数の型を使用する場合に機能しますが、Word8 を使用すると失敗することに注意してB.unpackください。では、なぜ失敗するのでしょうか。答えはCodec.Utils.toTwosCompのソース コードにあります。xが引数である を呼び出すことがわかります。toBase 256 (abs x)

—の型はCodec.Utils モジュールからエクスポートされておらず、ソースに明示的な型署名もありませんが、定義をファイルに入れ、GHCi にその型 ( )toBaseを尋ねることで確認できます。:t toBase

toBase :: (Integral a, Num b) => a -> a -> [b]

したがって、型に明示的に注釈を付けることは、toTwosCompを呼び出すことtoBase (256 :: Word8) (abs x :: Word8)です。なに256 :: Word8

GHCi> 256 :: Word8
0

おっとっと!256 > 255 なので、Word8 では保持できず、静かにオーバーフローします。toBase、基数変換の過程で、使用されている基数で除算されるため、最終的にゼロで除算され、取得している動作が生成されます。

解決策は何ですか?fromIntegralに渡す前に、Word8s を Ints に変換しますtoTwosComp

convert :: B.ByteString -> [Octet]
convert = map convert' . B.unpack
  where convert' b = head $ toTwosComp (fromIntegral b :: Int)

toTwosComp個人的には、この動作は少し心配です。あらゆるサイズの整数型で機能するように、おそらく整数への変換自体を行う必要があると思います。しかし、これは開発者が好まない可能性のあるパフォーマンスの低下を招きます。それでも、これは理解するためにソース ダイビングを必要とするかなり紛らわしい失敗です。ありがたいことに、回避するのは非常に簡単です。

于 2011-12-17T07:48:21.667 に答える
5
map (head . toTwosComp) [1, 2, 3, 4]

正常に動作します

map (head . toTwosComp) $ B.unpack $ B.pack [1, 2, 3, 4]

説明した例外が発生します。違いを見てみましょう。

> :t [1, 2, 3, 4]
[1, 2, 3, 4] :: Num t => [t]
> :t unpack $ pack $ [1, 2, 3, 4]
unpack $ pack $ [1,2,3,4] :: [Word8]

Word8 が問題を引き起こしている可能性があります。どれどれ

> toTwosComp (1 :: Word8)
*** Exception: divide by zero

どうやら Word8 を他の整数型に変換する必要があるようです。

> map (head . toTwosComp . fromIntegral) $ B.unpack $ B.pack [1, 2, 3, 4]
[1,2,3,4]

できます!

于 2011-12-17T07:49:31.590 に答える