简体   繁体   English

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

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

Consider the following code that is supposed to print out random numbers:考虑以下应该打印出随机数的代码:

import System.Random.Mersenne

main =
 do g <- (newMTGen Nothing)
    xs <- (randoms g) :: IO [Double]
    mapM_ print xs  

When run, I get a segmentation fault error.运行时,出现分段错误。 That is unsurprising, since the function 'randoms' produces an infinite list.这并不奇怪,因为 function 'randoms' 产生了一个无限列表。 Suppose I wanted to print out only the first ten values of xs.假设我只想打印出 xs 的前十个值。 How could I do that?我怎么能那样做? xs has type IO [Double], and I think I want a variable of type [IO Double]. xs 的类型为 IO [Double],我想我想要一个 [IO Double] 类型的变量。 What operators exist to convert between the two.存在哪些运算符可以在两者之间进行转换。

If you get a segmentation fault error, and you didn't use the FFI or any functions with unsafe in their name, that's not unsurprising, in any situation, It means there's a bug with either GHC.如果您遇到分段错误,并且您没有使用 FFI 或名称中带有unsafe的任何函数,那么这并不奇怪,在任何情况下,这都意味着 GHC 存在错误。 or a library you're using is doing something unsafe.或者您正在使用的图书馆正在做一些不安全的事情。

Printing out an infinite list of Double s with mapM_ print is perfectly fine;使用mapM_ print打印出Double的无限列表非常好; the list will be processed incrementally and the program should run with constant memory usage.该列表将逐步处理,程序应以恒定的 memory 使用率运行。 I suspect there is a bug in the System.Random.Mersenne module you're using, or a bug the C library it's based on, or a problem with your computer (such as faulty RAM).我怀疑您正在使用的System.Random.Mersenne模块中存在错误,或者它所基于的 C 库存在错误,或者您的计算机存在问题(例如 RAM 故障)。 1 Note that newMTGen comes with this warning: 1请注意, newMTGen附带此警告:

Due to the current SFMT library being vastly impure, currently only a single generator is allowed per-program.由于当前的 SFMT 库非常不纯,目前每个程序只允许使用一个生成器。 Attempts to reinitialise it will fail.尝试重新初始化它将会失败。

You might be better off using the provided global MTGen instead.您最好改用提供的全局MTGen

That said, you can't convert IO [Double] into [IO Double] in that way;也就是说,您不能以这种方式将IO [Double]转换为[IO Double] there's no way to know how long the resulting list would be without executing the IO action, which is impossible, since you have a pure result (albeit one that happens to contain IO actions).如果不执行IO操作,就无法知道结果列表的长度,这是不可能的,因为您有一个纯结果(尽管恰好包含IO操作)。 For infinite lists, you could write:对于无限列表,您可以这样写:

desequence :: IO [a] -> [IO a]
desequence = desequence' 0
  where
    desequence n m = fmap (!! n) m : desequence (n+1) m

But every time you execute an action in this list, the IO [a] action would be executed again;但是每次执行这个列表中的动作时, IO [a]动作都会被再次执行; it'd just discard the rest of the list.它只会丢弃列表中的 rest。

The reason randoms can work and return an infinite list of random numbers is because it uses lazy IO with unsafeInterleaveIO . randoms可以工作并返回随机数的无限列表的原因是因为它使用带有 unsafeInterleaveIO 的惰性unsafeInterleaveIO (Note that, despite the "unsafe" in the name, this one can't cause segfaults, so something else is afoot.) (请注意,尽管名称中带有“不安全”字样,但这并不会导致段错误,因此还有其他事情正在发生。)

1 Other, less likely possibilities include a miscompilation of the C library, or a bug in GHC. 1其他不太可能的可能性包括 C 库的错误编译,或 GHC 中的错误。

Suppose I wanted to print out only the first ten values of xs.假设我只想打印出 xs 的前十个值。 How could I do that?我怎么能那样做?

Just use take :只需使用take

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

You wrote你写了

xs has type IO [Double] xs 的类型为 IO [Double]

But actually, randoms g has type IO [Double] , but thanks to the do notation, xs has type [Double] , you can just apply take 10 to it.但实际上, randoms g的类型为IO [Double] ,但由于do表示法, xs的类型为[Double] ,您只需对其应用take 10即可。

You could also skip the binding using liftM :您还可以使用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