更新:好的,这个问题可能变得非常简单。
q <- mapM return [1..]
为什么这永远不回来?
mapM是否不会懒惰地处理无限列表?
下面的代码挂起。但是,如果我用B线代替A线,它将不再挂起。另外,如果我在行A前面加上“splitRandom $”,它也不会挂起。
Q1是:mapM不懒惰吗?否则,为什么用行B替换行A可以“修复此”代码?
Q2是:为什么前面带有splitRandom的行“解决”问题?
import Control.Monad.Random
import Control.Applicative
f :: (RandomGen g) => Rand g (Double, [Double])
f = do
b <- splitRandom $ sequence $ repeat $ getRandom
c <- mapM return b -- A
-- let c = map id b -- B
a <- getRandom
return (a, c)
splitRandom :: (RandomGen g) => Rand g a -> Rand g a
splitRandom code = evalRand code <$> getSplit
t0 = do
(a, b) <- evalRand f <$> newStdGen
print a
print (take 3 b)
该代码懒惰地生成无限数量的随机数。然后,它生成一个随机数。通过使用splitRandom,我可以先在无限列表之前评估后一个随机数。如果我在函数中返回b而不是c可以证明这一点。
但是,如果我将mapM应用于列表,则程序现在挂起。为防止这种情况挂起,我必须在mapM之前再次应用splitRandom。我的印象是mapM可以懒惰
最佳答案
好吧,有一个懒惰,然后有一个懒惰。 mapM
确实是懒惰的,因为它所做的工作并不多。但是,请查看类型签名:
mapM :: (Monad m) => (a -> m b) -> [a] -> m [b]
考虑一下这意味着什么:给它提供一个函数
a -> m b
和一堆a
。常规的map
可以将它们变成一堆m b
,但不能变成m [b]
。在没有monad妨碍的情况下将b
组合成单个[b]
的唯一方法是使用>>=
将m b
排序在一起以构造列表。实际上,
mapM
与sequence . map
完全等效。通常,对于任何一元表达式,如果根本不使用该值,则必须强制导致该表达式的整个
>>=
链,因此无法将sequence
应用于无限列表。如果要使用无限制的单调序列,则需要显式的流控制-例如,以某种方式烘焙到绑定(bind)链中的循环终止条件,而
mapM
和sequence
这样的简单递归函数则无法提供-或分步执行的顺序,如下所示:data Stream m a = Nil | Stream a (m (Stream m a))
...这样您就可以仅根据需要强制使用多个monad层。
编辑:: 关于
splitRandom
,正在发生的事情是,您将它传递给Rand
计算,并使用种子splitRandom
进行评估,然后得到return
来计算结果。没有splitRandom
,单个getRandom
所使用的种子必须来自对无限列表进行排序的最终结果,因此将其挂起。使用额外的splitRandom
,只需通过两个splitRandom
调用即可使用所使用的种子,因此它可以工作。最终的随机数列表之所以有用,是因为您此时已离开了Rand
monad,并且没有任何内容取决于其最终状态。关于haskell - Haskell的mapM不偷懒吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/3270255/