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

Gau*_*tam 9 random io monads haskell

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

import System.Random.Mersenne

main =
 do g <- (newMTGen Nothing)
    xs <- (randoms g) :: IO [Double]
    mapM_ print xs  
Run Code Online (Sandbox Code Playgroud)

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

ehi*_*ird 11

如果你得到一个分段错误,而你没有使用FFI或任何功能用unsafe在他们的名字,这令人吃惊,在任何情况下!这意味着GHC或您正在使用的库正在做一些不安全的错误.

打印出无限的Doubles 列表mapM_ print非常精细; 列表将以递增方式处理,程序应以恒定的内存使用率运行.我怀疑System.Random.Mersenne您正在使用的模块中存在错误,或者它所基于的C库存在错误,或者您的计算机存在问题(例如RAM故障).1请注意newMTGen此警告附带:

由于当前的SFMT库非常不纯,目前每个程序只允许一个发生器.尝试重新初始化它将失败.

您可能最好使用提供的全局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
Run Code Online (Sandbox Code Playgroud)

但是每次在此列表中执行操作时,IO [a]操作都会再次执行; 它只是丢弃列表的其余部分.

原因randoms可以工作并返回无限的随机数列表是因为它使用了惰性IO unsafeInterleaveIO.(请注意,尽管名称中存在"不安全",但这个不会导致段错误,所以还有其他事情正在发生.)

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

  • 仅仅为了记录,我认为提问者的计算机可能出现问题,而不是图书馆; 提供的代码对我来说不是段落错误. (3认同)
  • +1"你不能将`IO [Double]`转换为`[IO Double]`...没有办法知道结果列表在没有执行IO动作的情况下会持续多长时间" (3认同)
  • @Gautam:当然有,只需在结果列表中使用`take`:`mapM_ print(需要10 xs)`. (3认同)

Nor*_*sey 11

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

只需使用take:

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

你写了

xs有类型IO [双]

但实际上,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
Run Code Online (Sandbox Code Playgroud)