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