为什么这段代码除以零?

Lit*_*rum 16 haskell divide-by-zero

我有一个小的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]
Run Code Online (Sandbox Code Playgroud)

谁能帮我理解这里发生了什么?

ehi*_*ird 17

我们可以减少这个

GHCi> toTwosComp (1 :: Word8)
*** Exception: divide by zero
Run Code Online (Sandbox Code Playgroud)

请注意,如果您使用Word16,Int,Integer或任意数量的类型,但在使用Word8时失败,则可以正常工作B.unpack!那为什么会失败呢?答案可以在Codec.Utils.toTwosComp源代码找到.你可以看到它调用toBase 256 (abs x),其中x是参数.

类型toBase- 不是从Codec.Utils模块导出的,并且在源代码中没有显式类型签名,但是您可以通过将定义放在文件中并询问GHCi类型是什么(:t toBase)来看到这一点,是

toBase :: (Integral a, Num b) => a -> a -> [b]
Run Code Online (Sandbox Code Playgroud)

因此,明确地注释类型toTwosComp是调用toBase (256 :: Word8) (abs x :: Word8).什么256 :: Word8

GHCi> 256 :: Word8
0
Run Code Online (Sandbox Code Playgroud)

哎呀!256> 255,所以我们不能在Word8中保存它,它会无声地溢出.toBase,在其基本转换的过程中,除以所使用的基数,因此它最终除以零,产生你得到的行为.

解决方案是什么?在将Word8s fromIntegral传递给Int 之前将其转换为Ints toTwosComp:

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

就个人而言,这种行为让我有点担心,我认为本toTwosComp应该做一个转换本身,可能是整数,所以它适用于各种大小的整数类型; 但这会导致性能损失,开发人员可能不喜欢这个想法.尽管如此,这是一个令人困惑的失败,需要潜水源才能理解.值得庆幸的是,它很容易解决.


Jan*_*Jan 5

map (head . toTwosComp) [1, 2, 3, 4]
Run Code Online (Sandbox Code Playgroud)

工作得很好

map (head . toTwosComp) $ B.unpack $ B.pack [1, 2, 3, 4]
Run Code Online (Sandbox Code Playgroud)

导致您描述的异常.让我们看看有什么区别.

> :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]
Run Code Online (Sandbox Code Playgroud)

Word8可能会导致问题.让我们来看看

> toTwosComp (1 :: Word8)
*** Exception: divide by zero
Run Code Online (Sandbox Code Playgroud)

显然我们必须将Word8转换为其他整数类型.

> map (head . toTwosComp . fromIntegral) $ B.unpack $ B.pack [1, 2, 3, 4]
[1,2,3,4]
Run Code Online (Sandbox Code Playgroud)

有用!