Haskell列表理解通过列表Monad理解



我来自python世界,但尽量使用函数式,并改变我的命令式思维。

现在我研究哈斯克尔,发现

list = [(x,y) | x<-[1,2,3], y<-[4,5,6]]

被翻译成

list' = 
[1,2,3] >>= x ->
[4,5,6] >>= y ->
return (x,y)

我尝试逐步了解列表 monad 绑定链的分步处理:

绑定去查找为:

xs >>= f = concat (map f xs)

并且绑定是左关联

因此,正如我所理解的,在开始时首先执行绑定([1,2,3]>>= \x ->

[4,5,6]),结果为[4,5,6,4,5,6,4,5,6]然后下一个绑定 [4,5,6,4,5,6,4,5,6]>>= \y -> 返回 (x,y) 已执行

但是,如果它全部计算出来,它如何在 lambda 中看到 x? x 只是根本没有具体值的参数(lambda 可以随时用 varios 参数调用,我们如何在外面看到它并修复??如果它以某种方式可以看到它,它怎么知道 x 调用历史记录被 1,2,3 更改了?在我的理解中,一旦第一次绑定计算完成,只有结果 [4,5,6,4,5,6,4,5,6] 可用,它进入下一个绑定的第一个参数。

所以我不明白我如何阅读这个结构,以及它如何一步一步地逻辑产生正确的结果?

这是混淆的常见来源。lambda 的范围尽可能扩展,因此:

[1,2,3] >>= x ->
[4,5,6] >>= y ->
return (x,y)

相当于这个:

[1,2,3] >>= (x ->
[4,5,6] >>= (y ->
return (x,y)))

所以内 lambday -> …在外 lambda 的范围内,x -> …,这意味着xy都在范围内。然后,您可以内联[]monad 实例的>>=return定义,并逐步完成评估:

concatMap (x ->
concatMap (y ->
[(x,y)]) [4,5,6]) [1,2,3]
concat
[ concatMap (y -> [(1,y)]) [4,5,6]
, concatMap (y -> [(2,y)]) [4,5,6]
, concatMap (y -> [(3,y)]) [4,5,6]
]
concat
[ concat [[(1,4)], [(1,5)], [(1,6)]]
, concat [[(2,4)], [(2,5)], [(2,6)]]
, concat [[(3,4)], [(3,5)], [(3,6)]]
]
concat
[ [(1,4), (1,5), (1,6)]
, [(2,4), (2,5), (2,6)]
, [(3,4), (3,5), (3,6)]
]
[ (1,4), (1,5), (1,6)
, (2,4), (2,5), (2,6)
, (3,4), (3,5), (3,6)
]

一种常见且更简洁的表达方式是使用Applicative实例而不是Monad实例,使用<$>(fmap) 和<*>(ap) 运算符或liftA2

(,) <$> [1,2,3] <*> [4,5,6]
liftA2 (,) [1,2,3] [4,5,6]

最新更新