繁体   English   中英

Haskell monad:IO [Double] 到 [IO Double]

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM