无限随机序列使用randomIO循环,但不使用getRandom [英] Infinite random sequence loops with randomIO but not with getRandom

查看:154
本文介绍了无限随机序列使用randomIO循环,但不使用getRandom的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很难找出一种推理方式来解释为什么以下两个看似等价的无限随机数序列( inf 和<$ c

$ $ p $ import Control.Monad.Random(Rand c> inf')完全不同: ,evalRandIO,getRandom)
import System.Random(Random,RandomGen,randomIO)
$ b $ inf ::(RandomGen g,Random a)=> Rand g [a]
inf = sequence(repeat getRandom)

inf'::(Random a)=> IO [a]
inf'= sequence(repeat randomIO)

- OK
main = do
i < - evalRandIO inf
putStrLn $ show $ take 5(i :: [Int])

- HANGS
main'= do
i < - inf'
putStrLn $ show $ take 5( i :: [Int])

被调用时, main'终止并打印5个随机整数,而 main 无限循环 - 导致序列的原因。重复 getRandom 上的计算方式不同于 randomIO ?

解决方案

排序列表在IO monad中是严格的,但在状态monad中可能是惰性的。 兰德只是一个包裹 StateT ,所以可能很懒惰:

  type Rand g = RandT g Identity 
newtype RandT gma = RandT(StateT gma)

evalRandIO 在一开始就查询IO随机数发生器,然后运行 State 对获得的 StdGen 进行有效计算:

  evalRandT ::(Monad m)=> RandT g m a  - > g  - > m a 
evalRandT(RandT x)g = evalStateT x g

evalRand :: Rand g a - > g - > a
evalRand x g = runIdentity(evalRandT x g)

evalRandIO :: Rand StdGen a - > IO a
evalRandIO x = fmap(evalRand x)newStdGen

相比之下, sequence $ repeat randomIO 包含无限数量的副作用,因为每个 randomIO 修改全局随机数生成器。我们无法检查返回值,直到执行所有效果。它类似于执行 sequence $ repeat getLine ,它只是反复读取行,并且永远不会返回。

I'm having difficulty trying to figure out a way to reason about why the following two, seemingly equivalent definitions of an infinite random number sequence (inf and inf') are evaluated completely differently:

import Control.Monad.Random (Rand, evalRandIO, getRandom)
import System.Random        (Random, RandomGen, randomIO)

inf :: (RandomGen g, Random a) => Rand g [a]
inf = sequence (repeat getRandom)

inf' :: (Random a) => IO [a]
inf' = sequence (repeat randomIO)

-- OK
main = do
  i <- evalRandIO inf
  putStrLn $ show $ take 5 (i :: [Int])

-- HANGS
main' = do
  i <- inf'
  putStrLn $ show $ take 5 (i :: [Int])

when called, main' terminates and prints 5 random integers, whereas main loops infinitely — what causes sequence . repeat to be evaluated differently on getRandom than it does on randomIO?

解决方案

Sequencing lists is strict in the IO monad but possibly lazy in the State monad. Rand is just a wrapped StateT, so it can be lazy:

type Rand g = RandT g Identity
newtype RandT g m a = RandT (StateT g m a)

evalRandIO queries the IO random number generator just once at the beginning, then runs the State-ful computation on the acquired StdGen:

evalRandT :: (Monad m) => RandT g m a -> g -> m a
evalRandT (RandT x) g = evalStateT x g

evalRand :: Rand g a -> g -> a
evalRand x g = runIdentity (evalRandT x g)

evalRandIO :: Rand StdGen a -> IO a
evalRandIO x = fmap (evalRand x) newStdGen

In contrast, sequence $ repeat randomIO contains an infinite number of side effecting IO actions, because each randomIO modifies the global random number generator. We can't inspect the return value until all the effects are performed. It's similar to doing sequence $ repeat getLine, which just reads lines repeatedly, and never returns.

这篇关于无限随机序列使用randomIO循环,但不使用getRandom的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆