2011-12-17 33 views
16

tôi có một chương trình Haskell nhỏ, và tò mò tại sao một chia cho số không ngoại lệ được ném khi tôi chạy nó (GHC 7.0.3)Tại sao mã này chia cho số không?

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] 

thể bất cứ ai giúp tôi hiểu những gì đang xảy ra ở đây?

+2

Đó là lỗi trong 'toTwosComp'. – augustss

Trả lời

17

Chúng ta có thể giảm xuống còn

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

Lưu ý rằng công trình này nếu bạn sử dụng Word16, Int, Integer, hoặc bất kỳ số lượng các loại, nhưng không thành công khi sử dụng Word8, như B.unpack cho chúng ta! Vậy tại sao nó lại thất bại? Câu trả lời được tìm thấy trong số source code đến Codec.Utils.toTwosComp. Bạn có thể thấy rằng nó gọi số toBase 256 (abs x), trong đó x là đối số.

Loại toBase - không được xuất từ ​​mô-đun Codec.Utils và không có chữ ký rõ ràng trong nguồn, nhưng bạn có thể thấy điều này bằng cách đặt định nghĩa trong tệp và yêu cầu GHCi loại đó là gì (:t toBase) , là

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

Vì vậy, chú thích loại rõ ràng, toTwosComp đang gọi toBase (256 :: Word8) (abs x :: Word8). 256 :: Word8 là gì?

GHCi> 256 :: Word8 
0 

Rất tiếc! 256> 255, vì vậy chúng tôi không thể giữ nó trong một Word8, và nó tràn âm thầm. toBase, trong quá trình chuyển đổi cơ sở của nó, chia cho các cơ sở đang được sử dụng, do đó, nó kết thúc lên chia cho số không, sản xuất các hành vi bạn đang nhận được.

Giải pháp là gì? Chuyển đổi Word8s để Ints với fromIntegral trước khi đi qua họ toTwosComp:

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

Cá nhân, hành vi này làm tôi lo lắng một chút, và tôi nghĩ rằng toTwosComp có lẽ nên làm như vậy một chuyển đổi bản thân, khả năng Integer, để nó làm việc với loại tích phân của mọi kích thước; nhưng điều này sẽ phải chịu một hình phạt hiệu suất mà các nhà phát triển có thể không thích ý tưởng. Tuy nhiên, đây là một thất bại khá khó hiểu đòi hỏi nguồn lặn để hiểu. Rất may, nó rất dễ dàng để làm việc xung quanh.

+0

Siêu hữu ích! Cảm ơn rất nhiều: D – Litherum

5
map (head . toTwosComp) [1, 2, 3, 4] 

hoạt động tốt trong khi

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

gây ra ngoại lệ bạn đã mô tả. Hãy xem sự khác biệt là gì.

> :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 có thể đang gây ra sự cố. Hãy xem

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

Vì vậy, dường như chúng tôi phải chuyển đổi Word8 thành loại số nguyên khác.

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

Nó hoạt động!

Các vấn đề liên quan