[英]Haskell monad: IO [Double] to [IO Double]
考虑以下应该打印出随机数的代码:
import System.Random.Mersenne
main =
do g <- (newMTGen Nothing)
xs <- (randoms g) :: IO [Double]
mapM_ print xs
运行时,出现分段错误。 这并不奇怪,因为 function 'randoms' 产生了一个无限列表。 假设我只想打印出 xs 的前十个值。 我怎么能那样做? xs 的类型为 IO [Double],我想我想要一个 [IO Double] 类型的变量。 存在哪些运算符可以在两者之间进行转换。
如果您遇到分段错误,并且您没有使用 FFI 或名称中带有unsafe
的任何函数,那么这并不奇怪,在任何情况下,这都意味着 GHC 存在错误。 或者您正在使用的图书馆正在做一些不安全的事情。
使用mapM_ print
打印出Double
的无限列表非常好; 该列表将逐步处理,程序应以恒定的 memory 使用率运行。 我怀疑您正在使用的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
但是每次执行这个列表中的动作时, IO [a]
动作都会被再次执行; 它只会丢弃列表中的 rest。
randoms
可以工作并返回随机数的无限列表的原因是因为它使用带有 unsafeInterleaveIO 的惰性unsafeInterleaveIO
。 (请注意,尽管名称中带有“不安全”字样,但这并不会导致段错误,因此还有其他事情正在发生。)
1其他不太可能的可能性包括 C 库的错误编译,或 GHC 中的错误。
假设我只想打印出 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]
,您只需对其应用take 10
即可。
您还可以使用liftM
跳过绑定:
main =
do g <- newMTGen Nothing
ys <- liftM (take 10) $ randoms g :: IO [Double]
mapM_ print ys
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.