【问题标题】:Haskell monad: IO [Double] to [IO Double]Haskell monad:IO [Double] 到 [IO Double]
【发布时间】:2012-04-18 13:29:34
【问题描述】:

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

import System.Random.Mersenne

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

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

【问题讨论】:

  • 顺便说一下,IO [Double] -> [IO Double] 本质上是“序列”的反向类型签名。
  • 听起来像是某种错误编译或硬件问题,那么...您可能需要运行memtest86+ 检查。
  • 你可以做一个IO [Double] -&gt; IO [IO Double] ...除非你unsafePerformIO
  • 那个流浪的-}很不寻常。
  • Daniel,您的代码是否正常工作?并且 } 是一个错字

标签: haskell random io monads


【解决方案1】:

假设我只想打印 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

【讨论】:

    【解决方案2】:

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

    mapM_ print 打印出Doubles 的无限列表非常好;该列表将被增量处理,并且程序应该以恒定的内存使用量运行。我怀疑您正在使用的 System.Random.Mersenne 模块中存在错误,或者它所基于的 C 库存在错误,或者您的计算机存在问题(例如 RAM 故障)。1 请注意newMTGen 带有此警告:

    由于当前的 SFMT 库非常不纯,目前每个程序只允许使用一个生成器。尝试重新初始化它会失败。

    您最好改用提供的global 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 可以工作并返回一个无限的随机数列表的原因是因为它使用了 unsafeInterleaveIO 的惰性 IO。 (请注意,尽管名称中有“不安全”,但这个不能导致段错误,所以其他的事情正在进行中。)

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

    【讨论】:

    • 只是为了记录,我认为可能是提问者的电脑出了问题,而不是图书馆;提供的代码对我来说没有段错误。
    • +1 表示“您无法将 IO [Double] 转换为 [IO Double]...如果不执行 IO 操作,则无法知道结果列表将持续多长时间”
    • 所以没有办法只访问前十个列表元素?
    • @Gautam:当然可以,只需在结果列表中使用takemapM_ print (take 10 xs)
    • 这没有回答一个直接的问题(大约 10 个值),也没有纠正 OP 对类型的基本误解,而是讨论了一个错误的问题,好像它在这里有优点一样。 (-1)
    猜你喜欢
    • 1970-01-01
    • 2018-12-16
    • 1970-01-01
    • 1970-01-01
    • 2014-03-17
    • 2015-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多