考虑下面打算输出随机数的代码:
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 :(得分: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