gpt4 book ai didi

haskell - Haskell 的 mapM 不懒吗?

转载 作者:行者123 更新时间:2023-12-03 13:12:35 25 4
gpt4 key购买 nike

更新:好的,这个问题可能变得非常简单。

q <- mapM return [1..]

为什么这永远不会回来?

mapM 不会懒惰地处理无限列表吗?

下面的代码挂起。但是,如果我用 B 行替换 A 行,它就不再挂起。或者,如果我在 A 行前面加上“splitRandom $”,它也不会挂起。

Q1是:mapM不偷懒吗?否则,为什么用 B 行替换 A 行“修复此”代码?

Q2 是:为什么前面的 A 行和 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 s。一个普通的 map可以把那些变成一堆 m b s,但不是 m [b] .组合 b 的唯一方法s 成单个 [b]没有 monad 的阻碍是使用 >>=m b 进行排序s 一起构建列表。

事实上, mapM正好等价于 sequence . map .

一般来说,对于任何一元表达式,如果使用该值, >>= 的整个链s 导致表达式必须是强制的,所以应用 sequence一个无限的列表永远无法完成。

如果你想使用一个无界的单子(monad)序列,你要么需要显式的流控制——例如,以某种方式将循环终止条件烘焙到绑定(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/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com