2012-04-04 20 views
9

Rozważmy następujący kod, który ma wydrukować liczb losowych:Haskell monada: IO [Double] do [IO Double]

import System.Random.Mersenne 

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

Po uruchomieniu pojawia się błąd błędu segmentacji. Nie jest to zaskakujące, ponieważ funkcja "randoms" tworzy nieskończoną listę. Załóżmy, że chcę wydrukować tylko pierwsze dziesięć wartości xs. Jak mogłem to zrobić? xs ma typ IO [Double] i myślę, że chcę zmienną typu [IO Double]. Jakie operatory istnieją, aby dokonać konwersji między tymi dwoma.

+0

Nawiasem mówiąc, IO [dwukrotnie] -> [IO dwukrotnie] jest zasadniczo odwrócony podpis 'sekwencji'. – Gautam

+3

Tutaj nie występuje uszkodzenie. –

+1

Wygląda na to, że wystąpił jakiś błąd w sprzęcie lub problem ze sprzętem, a następnie ... możesz chcieć uruchomić test [memtest86 +] (http://www.memtest.org/). – ehird

Odpowiedz

11

Jeśli pojawi się błąd związany z błędem segmentacji i nie użyłeś FFI ani żadnych funkcji o numerze unsafe w nazwie, to jest to niezbyt zaskakujące, w każdej sytuacji! Oznacza to, że jest błąd z GHC lub biblioteka, z której korzystasz, robi coś niebezpiecznego.

Wydrukowanie nieskończonej listy Double s z mapM_ print jest całkowicie w porządku; lista będzie przetwarzana przyrostowo, a program powinien działać przy stałym wykorzystaniu pamięci. Podejrzewam, że jest błąd w module System.Random.Mersenne, którego używasz, lub błąd, na którym bazuje biblioteka C lub problem z twoim komputerem (np. Wadliwa pamięć RAM). Zauważ, że newMTGen pochodzi z tego ostrzeżenia:

Ze względu na obecny SFMT biblioteka jest bardzo zanieczyszczony, obecnie tylko jeden generator jest dozwolone per-programu. Próby reinicjowania go zawiodą.

Być może lepszym rozwiązaniem będzie korzystanie z podanego global MTGen.

Powiedziawszy, nie można w ten sposób przekształcić IO [Double] w [IO Double]; nie ma sposobu, aby wiedzieć, jak długo będzie wyświetlana lista wyników bez wykonywania akcji, która jest niemożliwa, ponieważ masz czysty wynik (choć jeden z nich zawiera akcje IO). Dla nieskończonych list, można napisać:

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

Ale za każdym razem, gdy wykonanie czynności w tej liście, akcja IO [a] będzie ponownie wykonywane; po prostu odrzuciłby resztę listy.

Powód, dla którego randoms może działać i zwracać nieskończoną listę liczb losowych, ponieważ używa on leniwego IO z unsafeInterleaveIO. (Zauważ, że mimo „niebezpieczne” w nazwie, ten nie może powodować naruszenia ochrony pamięci, więc coś innego się szykuje.)

Inne, mniej prawdopodobne możliwości obejmują miscompilation biblioteki C, lub błąd w GHC.

+3

Dla przypomnienia, myślę, że jest możliwe, że coś jest nie tak z komputerem pytającego, a nie z biblioteką; dostarczony kod nie stanowi dla mnie awarii. –

+3

+1 dla "nie można przekształcić' IO [Double] 'w' [IO Double] '... nie ma sposobu, aby wiedzieć, jak długo będzie wyświetlana lista wyników bez wykonywania akcji IO" –

+0

Więc nie ma mowy uzyskać dostęp tylko do pierwszych dziesięciu elementów listy? – Gautam

11

Załóżmy, że chcę wydrukować tylko pierwsze dziesięć wartości xs. Jak mogłem to zrobić?

Wystarczy użyć take:

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

Napisałeś

xs ma typu IO [Double]

Ale faktycznie, randoms g ma typ IO [Double], ale dzięki do notacja, xs ma typ [Double], możesz po prostu zastosować do niego take 10.

Można również pominąć wiązania, stosując liftM:

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