9

考虑以下应该打印出随机数的代码:

import System.Random.Mersenne

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

运行时,我收到分段错误错误。这并不奇怪,因为函数“randoms”会产生一个无限列表。假设我只想打印 xs 的前十个值。我怎么能那样做?xs 的类型为 IO [Double],我想我想要一个 [IO Double] 类型的变量。存在哪些运算符可以在两者之间进行转换。

4

2 回答 2

11

假设我只想打印 xs 的前十个值。我怎么能那样做?

只需使用take

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

你写了

xs 具有类型 IO [双]

但实际上,randoms ghas type IO [Double],但由于do符号,xshas type [Double],你可以应用take 10它。

您还可以使用以下方法跳过绑定liftM

main =
  do g <- newMTGen Nothing
     ys <- liftM (take 10) $ randoms g :: IO [Double]
     mapM_ print ys
于 2012-04-05T02:43:35.570 回答
11

如果您收到分段错误错误,并且您没有使用 FFI 或任何unsafe名称中的函数,这在任何情况下都不足为奇!这意味着 GHC 存在错误,或者您正在使用的库正在做一些不安全的事情。

打印出无限的Doubles列表mapM_ print非常好;该列表将被增量处理,并且程序应该以恒定的内存使用量运行。我怀疑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]操作都会再次执行;它只会丢弃列表的其余部分。

之所以randoms可以工作并返回一个无限的随机数列表是因为它使用惰性 IO 和unsafeInterleaveIO. (请注意,尽管名称中有“不安全”,但它不会导致段错误,因此正在发生其他事情。)

1其他不太可能的可能性包括 C 库的错误编译或 GHC 中的错误。

于 2012-04-04T19:30:14.557 回答