2012-04-04 30 views
9

Consideremos el siguiente código que se supone para imprimir números aleatorios:Haskell mónada: IO [doble] a [IO Doble]

import System.Random.Mersenne 

main = 
do g <- (newMTGen Nothing) 
    xs <- (randoms g) :: IO [Double] 
    mapM_ print xs 

Cuando se ejecuta, aparece un error de segmentación. Eso no es sorprendente, ya que la función 'randoms' produce una lista infinita. Supongamos que quisiera imprimir solo los primeros diez valores de xs. ¿Cómo podría hacer eso? xs tiene tipo IO [Doble], y creo que quiero una variable de tipo [IO doble]. Qué operadores existen para convertir entre los dos.

+0

Por cierto, IO [doble] -> [IO Doble] es esencialmente la firma de tipo inverso de la 'secuencia'. – Gautam

+3

No segfault aquí. –

+1

Suena como una compilación incorrecta de algún tipo o problema de hardware, entonces ... es posible que desee ejecutar un control [memtest86 +] (http://www.memtest.org/). – ehird

Respuesta

11

Si obtiene un error de segmentación, y que no ha utilizado la FFI o cualquier función con unsafe en su nombre, eso es no sorprendente, en cualquier situación! Significa que hay un error con cualquiera de los GHC, o que una biblioteca que estás usando está haciendo algo inseguro.

Imprimiendo una lista infinita de Double s con mapM_ print está perfectamente bien; la lista se procesará de forma incremental y el programa se ejecutará con uso de memoria constante. Sospecho que hay un error en el módulo System.Random.Mersenne que está utilizando, o un error de la biblioteca C en que se basa, o un problema con su computadora (como una RAM defectuosa). Tenga en cuenta que newMTGen viene con esta advertencia:

Debido a la biblioteca actual SFMT siendo muy impuro, actualmente sólo se permite un único generador por programa. Los intentos de reiniciarlo fracasarán.

En su lugar, es mejor que utilices el global MTGen provisto.

Dicho esto, no puede convertir IO [Double] en [IO Double] de esa manera; no hay forma de saber cuánto tiempo será la lista resultante sin ejecutar la acción IO, lo cual es imposible, ya que tiene un resultado puro (aunque uno que contiene IO acciones). Para las listas infinitas, se podría escribir:

desequence :: IO [a] -> [IO a] 
desequence = desequence' 0 
    where 
    desequence n m = fmap (!! n) m : desequence (n+1) m 

Pero cada vez que se ejecute una acción en esta lista, la acción IO [a] se ejecuta de nuevo; simplemente descartaría el resto de la lista.

El motivo randoms puede funcionar y devolver una lista infinita de números aleatorios porque usa IO perezosa con unsafeInterleaveIO. (Tenga en cuenta que, a pesar de la "inseguridad" en el nombre, éste no pueden los segfaults causa, así que algo más está en marcha.)

Otros, menos posibilidades probables incluyen un miscompilation de la biblioteca C, o un error en GHC.

+3

Solo para que quede constancia, creo que es posible que algo esté mal con la computadora del que pregunta, no con la biblioteca; el código proporcionado no segfault para mí. –

+3

+1 para "no se puede convertir' IO [Doble] 'en' [Doble IO] '... no hay forma de saber cuánto tiempo estará la lista resultante sin ejecutar la acción IO" –

+0

Así que no hay manera para acceder solo a los primeros diez elementos de la lista? – Gautam

11

Supongamos que deseo imprimir solo los primeros diez valores de xs. ¿Cómo podría hacer eso?

Sólo tiene que utilizar take:

main = 
do g <- (newMTGen Nothing) 
    xs <- (randoms g) :: IO [Double] 
    mapM_ print $ take 10 xs 

Usted escribió

xs tiene el tipo IO [doble]

Pero, en realidad, randoms g tiene tipo IO [Double], pero gracias a la Notación do, xs tiene tipo [Double], solo puede aplicar take 10 a él.

También podría saltarse la unión usando liftM:

main = 
    do g <- newMTGen Nothing 
    ys <- liftM (take 10) $ randoms g :: IO [Double] 
    mapM_ print ys