haskell列表理解通过列表monad理解

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了haskell列表理解通过列表monad理解相关的知识,希望对你有一定的参考价值。

我来自python世界,但尝试尽可能多地使用功能,并改变我的命令性思维。

现在我研究haskell并发现了

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

被翻译成

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

我试着逐步理解list 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 - > return(x,y)执行

但是如果它已经计算好了怎么能看到lambda里面的x? x只是根本没有具体值的参数(lambda可以在任何时候使用varios参数调用,我们如何将其置于外部并修复?)。如果它以某种方式可以看到它怎么能知道x呼叫历史是用1,2,3改变的?在我的理解第一次绑定计算完成后,只有结果[4,5,6,4,5,6,4,5,6]可用,接下来是下一个绑定的第一个参数。

所以我无法理解我如何阅读这种结构,以及它如何逐步逻辑地产生正确的结果?

答案

这是混淆的常见原因。 lambdas的范围尽可能扩展,所以这个:

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

相当于:

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

所以内部lambda y -> …在外部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]

以上是关于haskell列表理解通过列表monad理解的主要内容,如果未能解决你的问题,请参考以下文章

如何有条件地将元素插入列表?

列表理解不会在 Haskell 中给出正确的结果

在haskell中构建一个非确定性的monad转换器

函数式编程的 Monads,2.2 变体一:异常

Haskell 需要帮助理解流

Haskell Monad(下)