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中的错误.
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)