我对Haskell相当陌生,并且在这里查看此帖子:Haskell中2个列表的笛卡尔积。
在答案中有以下代码片段:
cartProd xs ys = [(x,y) | x <- xs, y <- ys]
与这两个列表:
xs = [1,2,3]
ys = [4,5,6]
会屈服
[(1,4),(1,5),(1,6),(2,4),(2,5),(2,6),(3,4),(3,5),(3,6)]
如果我没有看到这个结果,我会以为它会返回
[(1,4),(2,5),(3,6)]
因为它会同时遍历两个列表。
但是现在,对于我更了解的编程语言而言,它看起来像是用于遍历矩阵的double for循环:
for (int x = 1; x < 4; x++)
for(int y = 4; y < 7; y++)
//make tuple (x,y)
是什么导致列表理解以这种方式表现?
本简介介绍了列表理解的语法。基本上可以说,每种x <- list
方法都意味着一个附加的嵌套“ for”循环来生成元组,并且每个谓词都经过了简单的测试。因此表达式:
[(x,y) | x <- xs, even x, y <- ys, even 3*y-div x 2]
将以命令式语言翻译为:
for (var x : xs) {
if(even(x)) {
for(var y : ys) {
if(even(3*y-x/2)) {
yield (x,y)
}
}
}
yield
是有时与协同例程一起使用的关键字。此外,yield
评估是懒惰的。例如,这可以生成所有偶数整数,如下所示:
[x|x <-[2..],even x]
为了从根本上理解列表理解,需要知道什么是Monads。每个列表理解都可以转换为列表monad。例如,您的示例被翻译为:
do x <- xs
return
(do y <- ys
return (x,y))
这又是语法糖:
xs >>= (\x -> (ys >>= \y -> return (x,y)))
monad是函数式编程中的一个重要概念(也许最好阅读Wikipedia页面),因为它有点难以掌握。有时说一个单子都像卷饼,...。
一旦您或多或少地理解了monad:monad是带有return
语句和>>
通道语句的类型类。现在return
内部的语句很简单:
return x = [x]
这样就意味着每一次x
和y
设定,您将创建一个元组(x,y)
并返回它作为一个单独列表:这样[(x,y)]
。现在的“绑定”运营商>>=
需要的“胶水”ys
和\y -> return (x,y)
在一起。通过将其实现为:
(>>=) xs f = concat $ map f xs
换句话说,您进行映射并连接映射结果。
现在,如果考虑到未使用表达式的第二部分:
ys >>= \y -> return (x,y)
这意味着对于给定的x
(我们现在将其抽象掉),我们将把每个元素映射ys
到一个元组(x,y)
并返回它。因此,我们将生成一个列表列表,每个列表都是一个包含元组的单例。类似于(如果ys=[1,2]
):
[[(x,1)],[(x,2)]]
现在,>>=
它将进一步concat
变为:
\x -> [(x,1),(x,2)]
到现在为止,我们已经对抽象对象进行x
了抽象(假设它是一个)。但是现在我们可以采用该表达式的第一部分:
xs >>= \x -> [(x,1),(x,2)]
如果为xs=[3,5]
,则表示我们将再次创建列表:
[[(3,1),(3,2)],[(5,1),(5,2)]]
并在concat之后:
[(3,1),(3,2),(5,1),(5,2)]
我们期望的是:
[(x,y)|x<-[3,5],y<-[1,2]]
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句