理解守卫函数和列表推导式

4

我正在学习Miran Lipovaca的《Learn You a Haskell for Great Good!》这本书中关于guard函数的内容。

对于以下例子:

ghci> [1..50] >>= (\x -> guard('7' `elem` show x) >> return x)
[7, 17, 27, 37, 47]

我知道 `guard` 接受一个布尔值,如果该值为 `True`, `guard` 将使用最小默认上下文将 `()` 放入其中并成功。如果该值为 `False`,则 `guard` 生成失败的单子值。
然而,我不理解在上面的示例中 `guard` 如何创建结果列表 `[7, 17, 27, 37, 47]`。在 lambda 函数中传递的是什么作为 `x`,是1吗?此外,如果 `('7'`elem` show x)` 计算结果为 `False`,那么空列表将被返回吗?最终的结果列表是如何得到的?
1个回答

6
在列表 Monad 实例中:
  • >>= 是参数反转的 concatMap

  • guard condition 相当于 if condition then [()] else []

在任何 Monad 实例中,a >> b = a >>= \_ -> b,所以在列表实例中,这等同于 concatMap (\_ -> b) a

因此,您的代码转换为以下内容:

concatMap
  (\x -> concatMap
    (\_ -> [x])
    (if '7' `elem` show x then [()] else []))
  [1..50]

因此,外部的concatMap产生了一个中间值,这是一个由50个元素组成的列表,每个元素都是一个列表。如果其字符串表示包含数字7,则该列表是输入值的单例列表,否则为空列表:
[[], [], [], [], [], [], [7], [], [], [], [], [], [], [], [], [], [17], …]

然后将它们连接起来得到最终结果[7, 17, 27, 37, 47]

在 lambda 函数中传递的 x,是 1 吗?

x 是输入列表中的每个元素,从150

如果条件为真,则内部的 concatMap 生成 [x],如果条件为假,则生成 []。这是因为,当条件为真时,guard 会产生一个元素列表(虚拟的()),而如果为假则生成一个空列表。如果你将其重新表述为等效形式,可能会更容易理解:

map (\_ -> x) (if '7' `elem` show x then [()] else [])
-- or
if '7' `elem` show x then [x] else []

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接