【问题标题】:Printing x randomIO values from a list of randomIO's从 randomIO 列表中打印 x 个 randomIO 值
【发布时间】:2011-10-20 14:38:00
【问题描述】:

我已经收到了这个 sn-p 代码,应该解释它是非终止的,并提出一个可能的修复方案。

randomW =  do randomvalues <- sequence (repeat (randomIO :: IO Float))
              print (take 10 randomvalues)

修复的条件是不断生成一个无限列表,以便我们可以使用 take 函数。

我认为问题源于序列函数的不那么懒惰的性质,它试图到达repeat (randomIO :: IO Float)生成的列表的末尾,导致不终止。

我也不确定在 randomIO 上是否可以使用重复功能。

test = do random <- repeat (randomIO :: IO Float)
          print random

这会产生类型错误。 Print 似乎无法处理 IO Float,这似乎表明您可以在类型 IO Float 上使用 repeat。

【问题讨论】:

  • 第二个代码sn-p的问题是repeat randomIO的类型是[IO Float]。在做repeat randomIO &gt;&gt;= \random -&gt; ... 时,你实际上是在 list monad 中。

标签: haskell random io functional-programming


【解决方案1】:

所以:

repeat :: a -> [a]
randomIO :: Random a => IO a
sequence :: Monad m => [m a] -> m [a]

=>

repeat (randomIO :: IO Float) :: [IO Float]

所以当你这样做时:

random <- repeat (randomIO :: IO Float)

你实际上是在利用 list monad,所以 random 的类型为 IO Float。由于您在列表 monad 中,因此您的最后一条语句需要具有 [a] 类型,但它具有 IO () 类型,因为它是对 print 的调用,因此会出现类型错误。

整个序列的重点是将这个[IO a] 转换为一个IO [a],您可以执行它来获取随机值列表,并希望打印这个列表。现在,当您执行这样的 IO 时,需要一次执行所有操作,除非使用 unsafeInterleaveIO,在这种情况下不建议这样做。所以它试图获取那个无限列表......然后挂起(它可能会在某个时候堆栈溢出,我不确定)。

要获得一个无限的随机值列表,您不需要所有这些,只需要获得一个随机种子,并纯粹从种子中计算随机值。

您应该能够使用这些functions 构造一个无限的随机值列表:

randomIO :: Random a => IO a        -- to provide an IO Int
mkStdGen :: Int -> StdGen           -- to obtain a random generator from that Int
randoms :: RandomGen g => g -> [a]  -- to generate the infinite list

请注意,最后两个函数是纯函数。阅读this thread 可能会给您更多的想法。


编辑:

你应该如何使用mkStdGen的例子:

randomList :: Random a => IO [a]
randomList = do seed <- randomIO
                let gen = mkStdGen seed
                return (randoms gen)

我现在无法对其进行测试,但这应该可以。不过,您可能希望根据您的用例进行调整。

关于您的其他问题:

map :: (a -> b) -> [a] -> [b]
print :: Show a => a -> IO ()

=> 地图打印 :: Show a => [a] -> [IO()]

这可能不是你想要的,对吧? 如果你只是想打印一个列表,不需要mapprint可以处理列表。

【讨论】:

  • 感谢您的回复,我目前正在尝试理解您发布的主题。不过我有一个问题...当我尝试时:random2 = do print (take 10 (randoms (mkStdGen 1))) 它给了我类型错误,但是当我在 Prelude 中使用相同的 do 表达式时,它以某种方式完成了我希望它做...
  • 您需要为随机生成的值指定您期望的类型,因为上下文中没有任何内容可以帮助 GHC 确定它是什么。因此,如果您期望随机的Float 值列表,它应该是random2 = do print (take 10 (randoms (mkStdGen 1) :: [Float])) 。由于其类型默认功能,它可以在 GHCi 中使用,请参阅 haskell.org/ghc/docs/latest/html/users_guide/…
  • 我已经设法做到了这样...... random2 = let list = randoms (mkStdGen 1) :: [Float] in do print (take 10 list) 但这个任务的重点是使用 RandomIO 以在 IO monad 中获得一些副作用计算。话虽如此,因为序列不会削减它。因为它需要完全计算。是否可以在这个无限列表上使用地图(打印)?
  • @Shotor:你做错了!你不应该给1这样的常量种子mkStdGen,它总是会以这种方式返回完全相同的列表!你需要给它一个随机种子,这就是randomIO 的重点。有关详细信息,请参阅我编辑的消息。
  • 你是绝对正确的..我现在使用randomIO来生成一个int..但我仍然有一个类型错误:random2 = do seed >= ( \x -> print (take 10 (randoms (mkStdGen x) :: [Float] ))) .. 令我惊讶的是,这个版本几乎可以按我的意愿工作。我真的看不出 2 之间的区别
【解决方案2】:

您的第一个代码不起作用的原因是您尝试sequence 无限数量的IO 操作。由于这使用了严格的 IO,所以在所有操作都执行完毕之前,程序是不允许继续的,这将永远持续下去。

一个简单的解决方案是take对它们进行排序之前需要的操作数,例如:

 randomW = do values <- sequence (take 10 $ repeat (randomIO :: IO Float))
              print values

这可以用replicateM from Control.Monad 写得更简洁:

 randomW = do values <- replicateM 10 (randomIO :: IO Float)
              print values

或者,您可以使用randoms 根据单个随机种子制作无限的随机数列表(类似于 Ptival 的回答):

 randomW = do gen <- newStdGen
              let randomValues = randoms gen :: [Float]
              print (take 10 randomValues)

这里,我们只使用了一个 IO 动作,无限列表是基于这个惰性生成的,所以没有无限数量的副作用要运行。

【讨论】:

  • 总结一下:这行不通的原因是,当您使用 randomIO 时,您会得到一个 IO Float(在这种情况下),它本身或多或少是一个程序,它会打印本身。如果我们使用序列来执行所有这些命令,它永远不会终止,因为它需要到达列表的末尾。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-09
  • 1970-01-01
  • 2020-04-03
  • 1970-01-01
  • 1970-01-01
  • 2015-01-03
相关资源
最近更新 更多