que porque por pasa numero notacion minutos listas infinito funciones existe entre ejemplo división dividido denominador demuestre cuanto cuando comprension como comandos codigo cero casos cadenas basicos basico haskell divide-by-zero

haskell - porque - todo numero dividido por cero es



¿Por qué este código se divide por cero? (2)

Tengo un pequeño programa de Haskell y tengo curiosidad de por qué se produce una excepción de división por cero cuando lo ejecuto (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]

¿Puede alguien ayudarme a entender lo que está pasando aquí?


Podemos reducir esto a

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

Tenga en cuenta que esto funciona si usa Word16, Int, Integer o cualquier número de tipos, pero falla al usar Word8, ¡como nos da B.unpack ! Entonces, ¿por qué falla? La respuesta se encuentra en el código fuente de Codec.Utils.toTwosComp . Puedes ver que llama a la toBase 256 (abs x) , donde x es el argumento.

El tipo de toBase - no se exporta desde el módulo Codec.Utils, y sin una firma de tipo explícita en la fuente, pero puede ver esto al colocar la definición en un archivo y preguntar a GHCi qué tipo es ( :t toBase ), es

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

Entonces, anotando tipos explícitamente, toTwosComp está llamando a toBase (256 :: Word8) (abs x :: Word8) . ¿Qué es 256 :: Word8 ?

GHCi> 256 :: Word8 0

¡Uy! 256> 255, por lo que no podemos mantenerlo en Word8 y se desborda silenciosamente. toBase , en el proceso de conversión de su base, se divide por la base que se está utilizando, por lo que termina dividiéndose por cero, lo que produce el comportamiento que está obteniendo.

¿Cual es la solución? Convierta Word8s a Ints con fromIntegral antes de pasarlos a toTwosComp :

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

Personalmente, este comportamiento me preocupa un poco, y creo que toTwosComp probablemente debería hacer tal conversión, probablemente a Integer, para que funcione con tipos integrales de todos los tamaños; pero esto conllevaría una penalización de rendimiento que a los desarrolladores no les gustaría tener la idea. Aún así, este es un error bastante confuso que requiere que el buceo en origen se entienda. Afortunadamente, es muy fácil trabajar alrededor.


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

funciona bien mientras

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

causa la excepción que has descrito. Vamos a ver cuál es la diferencia.

> :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 podría estar causando el problema. Veamos

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

Así que aparentemente tenemos que convertir Word8 a otro tipo de entero.

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

¡Funciona!