2011-12-17 3 views
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] 

Может кто-нибудь помочь мне понять, что здесь происходит?

+2

Это ошибка в 'toTwosComp'. – augustss

ответ

17

Мы можем уменьшить это

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

Обратите внимание, что это работает, если вы используете Word16, Int, Integer, или любое количество типов, но терпит неудачу при использовании Word8 как B.unpack дает нам! Так почему это терпит неудачу? Ответ находится в source code до Codec.Utils.toTwosComp. Вы можете видеть, что он вызывает toBase 256 (abs x), где x является аргументом.

Тип toBase - не экспортируются из модуля Codec.Utils и без явного типа подписи в источнике, но вы можете видеть это, помещая определение в файле и спрашивать GHCI, что тип является (: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, в процессе его базового преобразования, делит на используемую базу, поэтому он заканчивается делением на ноль, создавая поведение, которое вы получаете.

Какое решение? Преобразование Word8s в Ints с fromIntegral перед передачей их toTwosComp:

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

Лично это поведение беспокоит меня немного, и я думаю, что toTwosComp, вероятно, следует сделать такое само преобразование, вероятно в целое, так что он работает с интегральные типы каждого размера; но это повлечет за собой штраф за производительность, который разработчикам может не понравиться. Тем не менее, это довольно запутанный провал, который требует понимания источника. К счастью, очень легко обойти.

+0

Супер полезно! Большое спасибо: D – Litherum

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] 

Слово8 может быть причиной проблемы. Давайте посмотрим

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

По-видимому, нам нужно преобразовать Word8 в другой целочисленный тип.

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

Это работает!

Смежные вопросы