Haskell monad:IO [Double] to [IO Double]

时间:2012-04-04 19:25:07

标签: haskell random io monads

考虑下面打算输出随机数的代码:

import System.Random.Mersenne

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

运行时,出现分段错误错误。这并不奇怪,因为函数'randoms'产生了无限的列表。假设我只打印出xs的前十个值。我怎么能这样做? xs的类型为IO [Double],我想我想要一个类型为[IO Double]的变量。存在哪些运算符可以在两者之间进行转换。

2 个答案:

答案 0 :(得分:11)

如果您遇到分段错误错误,并且您没有使用FFI或其名称中包含unsafe的任何功能,那么在任何情况下都不会出现 并不令人惊讶!这意味着GHC或您正在使用的库正在做一些不安全的错误。

使用Double打印出mapM_ print的无限列表完全没问题;列表将以递增方式处理,程序应以恒定的内存使用率运行。我怀疑你正在使用的System.Random.Mersenne模块中存在一个错误,或者它所基于的C库存在错误,或者计算机存在问题(例如RAM错误)。 1 请注意,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。 (请注意,尽管名称中存在“不安全”,但这个不能导致段错误,因此正在进行其他操作。)

1 其他可能性较小的可能性包括C库的错误编译或GHC中的错误。

答案 1 :(得分:11)

  

假设我只打印出xs的前十个值。我怎么能这样做?

只需使用take

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

你写了

  

xs的类型为IO [Double]

但实际上,randoms g的类型为IO [Double],但由于do符号,xs的类型为[Double],您只需应用{{1}对它来说。

您还可以使用take 10跳过绑定:

liftM