2011-12-17 16 views
16

Ho un piccolo programma Haskell, e sono curioso di sapere il motivo per cui una divisione per lo zero un'eccezione viene buttato quando l'eseguo (GHC 7.0.3)Perché questo codice si divide per zero?

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] 

Qualcuno può aiutarmi a capire cosa sta succedendo qui?

+2

È un errore in 'toTwosComp'. – augustss

risposta

17

Possiamo ridurre questo per

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

Nota che questo funziona se si utilizza Word16, Int, Integer, o un qualsiasi numero di tipi, ma non riesce quando si utilizza Word8, come B.unpack ci dà! Allora perché fallisce? La risposta si trova nello source code a Codec.Utils.toTwosComp. Puoi vedere che chiama lo toBase 256 (abs x), dove l'argomento è x.

Il tipo di toBase - non esportato dal modulo Codec.Utils, e senza una firma di tipo esplicito nella sorgente, ma si può vedere questo mettendo la definizione in un file e chiedendo GHCi ciò che il tipo è (:t toBase) , è

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

Quindi, annotare i tipi in modo esplicito, toTwosComp sta chiamando toBase (256 :: Word8) (abs x :: Word8). Cos'è 256 :: Word8?

GHCi> 256 :: Word8 
0 

Oops! 256> 255, quindi non possiamo tenerlo in un Word8, e trabocca silenziosamente. toBase, nel processo della sua conversione di base, divide per base utilizzata, quindi finisce dividendo per zero, producendo il comportamento che si sta ottenendo.

Qual è la soluzione? Convertire i Word8s a INT con fromIntegral prima di passarli a toTwosComp:

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

Personalmente, questo comportamento mi preoccupa un po ', e credo che toTwosComp dovrebbe probabilmente fare una conversione stessa, rischia di Integer, in modo che funzioni con tipi integrali di ogni dimensione; ma ciò comporterebbe una penalizzazione delle prestazioni a cui gli sviluppatori potrebbero non piacere l'idea di. Eppure, questo è un fallimento piuttosto confuso che richiede una fonte di immersione per capire. Per fortuna, è molto facile lavorare.

+0

Super utile! Grazie mille: D – Litherum

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

funziona bene, mentre

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

causa l'eccezione che hai descritto. Vediamo qual è la differenza.

> :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 potrebbe causare il problema. Vediamo

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

Quindi apparentemente dobbiamo convertire Word8 in un altro tipo intero.

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

Funziona!

Problemi correlati