2012-04-04 20 views
9

Si consideri il seguente codice che dovrebbe stampare numeri casuali:Haskell monade: IO [Doppia] a [IO Doppia]

import System.Random.Mersenne 

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

Quando viene eseguito, ottengo un errore di segmentazione. Ciò non sorprende, poiché la funzione "randoms" produce una lista infinita. Supponiamo che volessi stampare solo i primi dieci valori di xs. Come potrei farlo? xs ha tipo IO [Double], e penso di volere una variabile di tipo [IO Double]. Quali operatori esistono per convertire tra i due.

+0

Per inciso, IO [Doppia] -> [IO Doppia] è essenzialmente il tipo di firma contrario di 'sequenza'. – Gautam

+3

Qui non segfault. –

+1

Sembra una compitazione di qualche tipo o un problema hardware, quindi ... potresti voler eseguire un controllo [memtest86 +] (http://www.memtest.org/). – ehird

risposta

11

Se si ottiene un errore di segmentazione, e non è stata utilizzata l'FFI o qualsiasi funzione con unsafe in loro nome, questo è non sorprendente, in ogni situazione! Significa che c'è un errore con GHC, o una libreria che stai usando sta facendo qualcosa di non sicuro.

Stampare una lista infinita di Double s con mapM_ print è perfettamente a posto; la lista verrà elaborata in modo incrementale e il programma dovrebbe essere eseguito con un utilizzo costante della memoria. Sospetto che ci sia un bug nel modulo System.Random.Mersenne che stai usando, o un bug sulla libreria C su cui è basato, o un problema con il tuo computer (come la RAM difettosa). noti che newMTGen viene fornito con questo avvertimento:

A causa della corrente biblioteca SFMT essendo enormemente impura, attualmente solo un singolo generatore è consentito per-programma. I tentativi di reinizializzare falliranno.

Potrebbe essere preferibile utilizzare il global MTGen fornito.

Detto questo, non è possibile convertire IO [Double] in [IO Double] in questo modo; non c'è modo di sapere per quanto tempo la lista risultante sarebbe senza eseguire l'azione IO, che è impossibile, dal momento che si ha un risultato puro (anche se capita di contenere azioni IO). Per le liste infinite, si potrebbe scrivere:

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

Ma ogni volta che si eseguire un'azione in questo elenco, l'azione IO [a] sarebbe eseguita di nuovo; semplicemente scarterebbe il resto della lista.

Il motivo randoms può funzionare e restituire un elenco infinito di numeri casuali è perché utilizza IO pigro con unsafeInterleaveIO. (Si noti che, nonostante il "non sicuro" nel nome, questa non può causa segfaults, in modo da qualcosa altro è in corso di realizzazione.)

Altri, meno probabile possibilità includono un miscompilation della libreria C, o un bug in GHC.

+3

Solo per la cronaca, penso che sia possibile che qualcosa non sia nel computer del questionario, non nella libreria; il codice fornito non segfault per me. –

+3

+1 per "non puoi convertire' IO [Double] 'in' [IO Double] '... non c'è modo di sapere per quanto tempo la lista risultante sarebbe senza eseguire l'azione IO" –

+0

Quindi non c'è modo per accedere solo ai primi dieci elementi dell'elenco? – Gautam

11

Supponiamo di voler stampare solo i primi dieci valori di xs. Come potrei farlo?

Basta usare take:

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

hai scritto

xs ha digitate IO [Doppia]

Ma in realtà, randoms g ha tipo IO [Double], ma grazie alla do notazione, xs ha il tipo [Double], puoi semplicemente applicare take 10 ad esso.

si potrebbe anche saltare la rilegatura utilizzando liftM:

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