2012-04-04 3 views
9

Рассмотрим следующий код, который должен напечатать случайных чисел:Haskell монада: IO [Double] к [IO Double]

import System.Random.Mersenne 

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

При запуске, я получаю ошибку ошибки сегментации. Это неудивительно, так как функция «randoms» создает бесконечный список. Предположим, я хотел распечатать только первые десять значений xs. Как я мог это сделать? xs имеет тип IO [Double], и ​​я думаю, что мне нужна переменная типа [IO Double]. Какие операторы существуют для преобразования между ними.

+0

Кстати, IO [Double] -> [IO Double], по существу, обратный тип подписи 'последовательности'. – Gautam

+3

Здесь нет segfault. –

+1

Звучит как неправильная компиляция или проблема с оборудованием, тогда ... вы можете запустить проверку [memtest86 +] (http://www.memtest.org/). – ehird

ответ

11

Если вы получили ошибку ошибки сегментации, и вы не использовали FFI или какие-либо функции с unsafe на свое имя, это не неудивительно, в любой ситуации! Это означает, что есть ошибка с GHC, или библиотека, которую вы используете, делает что-то небезопасное.

Распечатка бесчисленного списка из Double s с mapM_ print отлично подходит; список будет обрабатываться постепенно и программа должна работать с постоянной памятью. Я подозреваю, что есть ошибка в модуле System.Random.Mersenne, который вы используете, или ошибка библиотеки C, на которой он основан, или проблема с вашим компьютером (например, неисправная ОЗУ). Обратите внимание, что newMTGen приходит с этим предупреждением:

Благодаря текущей SFMT библиотека будучи сильно нечистой, в настоящее время только один генератор допускается за программу. Попытки его повторной инициализации потерпят неудачу.

Возможно, вам лучше использовать предоставленный global MTGen.

Таким образом, вы не можете преобразовать IO [Double] в [IO Double] таким образом; нет никакого способа узнать, как долго результирующий список будет без выполнения действия IO, что невозможно, поскольку у вас есть чистый результат (хотя один из них содержит IO действия). Для бесконечных списков, вы могли бы написать:

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

Но каждый раз, когда вы выполняете действия в этом списке, то IO [a] действие будет выполнено снова; он просто отменил остальную часть списка.

Причина, по которой randoms может работать и возвращать бесконечный список случайных чисел, заключается в том, что он использует ленивый IO с unsafeInterleaveIO. (Обратите внимание, что, несмотря на «небезопасный» в названии, это один не может привести к ошибке сегментации, так что-то еще происходит.)

Другими, менее вероятно, возможности включают в себя неудачные библиотеках C, или ошибка в GHC.

+3

Просто для записи, я думаю, что возможно, что что-то не так с компьютером пользователя, а не с библиотекой; поставляемый код для меня не является секретным. –

+3

+1 для «вы не можете преобразовать' IO [Double] 'в' [IO Double] '... нет способа узнать, как долго результирующий список не будет выполняться при выполнении операции ввода-вывода« –

+0

Итак, нет способа для доступа только к первым десяти элементам списка? – Gautam

11

Предположим, что я хотел распечатать только первые десять значений xs. Как я мог это сделать?

Просто используйте take:

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

Вы писали

хз имеет тип IO [Double]

Но на самом деле, randoms g имеет тип IO [Double], но благодаря do обозначение, xs имеет тип [Double], вы можете просто применить take 10 к нему.

Вы также можете пропустить связывание с помощью liftM:

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