为什么 "and []" 是 True 而 "or []" 是 False?

11
为什么一个空列表上的“and”运算会返回True,这是否意味着一个空列表为True?很抱歉我无法正确理解这个问题,请纠正我。谢谢。
为什么空列表上的"and" 运算返回True,是否意味着空列表为True?对不起,我无法正确理解这个问题,请纠正我。谢谢。
Prelude> and []
True
Prelude> or []
False

13
两种情况下,答案都是该操作的恒等元。 - Frederick Cheung
@FrederickCheung,您能否扩展一下您的答案?抱歉,我不明白您所说的操作身份是什么意思。 - Rohit Sharma
如果您像这样阅读它:“如果 xs 的所有元素都为 true,则 xs 为 true”,那么这就是定义,这在您的示例中是成立的。而“如果其任何元素为 true,则 xs 为 true”则不适用于此示例。 - nicolas
7个回答

39
在数学中,谈论二元运算(例如&&||+*等)具有一个称为恒等元素的价值是很有用的。 恒等元素是一个值e,使得下面的性质对于一些通用的二元运算<>成立。
e <> x = x
x <> e = x

我列出的运算符是可交换的,也就是说对于所有的xy, x <> y = y <> x,因此我们只需要检查上述属性之一。 对于and运算,所涉及的二元运算符是&&,而对于or运算,其二元运算符为||。如果我们为这些操作制作Cayley表格,它会看起来像:

&&    | False | True
------+-------+------
False | False | False
True  | False | True


||    | False | True
------+-------+------
False | False | True
True  | True  | True

正如您所看到的,对于 &&,如果您有 True && FalseTrue && True,答案始终是 && 的第二个参数。 对于 ||,如果您有 False || FalseFalse || True,答案始终是第二个参数,因此每个参数的第一个参数必须是这些运算符下的恒等元素。 简而言之:

True && x = x
x && True = x

False || x = x
x || False = x
因此,当没有元素可执行操作时,每个运算的首选答案是恒等元素。
可能还有助于思考加法和乘法的恒等元素分别为0和1:
x + 0 = x = 0 + x
x * 1 = x = 1 * x

你也可以将这种操作扩展到列表拼接(使用++[]),针对类型为a -> a的函数进行函数组合(使用(.)id)以及许多其他操作。由于这开始看起来像是一种模式,你也许会问这是否已经在Haskell中成为了一种事实,事实上它确实是。模块Data.Monoid定义了抽象此模式的Monoid类型类,它的最小定义为

class Monoid a where
    mempty :: a                   -- The identity
    mappend :: a -> a -> a        -- The binary operator

它甚至将mappend别名为<>以便使用(选择这个名称作为通用二元运算符并非偶然)。我鼓励您查看该模块并尝试使用其定义。源代码非常易于阅读,可以启发人。


1
感谢 @bheklilr 抽出时间详细讲解。非常感激。 - Rohit Sharma

12

andor只是折叠函数,对于一个空列表调用折叠函数会生成其起始参数,分别是TrueFalse

如果加载了Prelude,它们将使用折叠函数来实现。否则,它们将使用显式递归来实现,即使不使用foldrfoldl,仍然可以将其视为折叠函数。通过查看源代码,我们可以看到它们的行为仍然相同:

and [] = True
and (x:xs) = x && and xs
or [] = False
or (x:xs) = x || or xs

这里提供了实现的链接。


为了消除评论中的混淆:折叠是一种函数,它接受一个二元函数和一个起始值(通常称为累加器),并遍历列表直到为空。当在空列表上调用时,折叠将返回原样的累加器,不管列表是否已经被遍历过。这是foldr的一个示例实现:

foldr _ acc [] = acc
foldr f acc (x:xs) = f x (foldr f acc xs)

and简单来说就是

and = foldr (&&) True

这使得and []的结果为True.


抱歉,但对于空列表来说,起始参数为空吗?那么在空列表上调用“and”折叠函数如何产生True呢? - Rohit Sharma
1
@RohitSharma 不,fold 的起始参数是第一个累加器值,对于“and”来说是“True”,对于“or”来说是“False”。 - ThreeFx
一个相关的注释是,XOR 没有身份元素,所以你不能(或者至少库不允许)定义 xor :: [Bool] -> Bool。然而,有 xor :: NonEmpty Bool -> Bool,它不需要身份元素,因为列表中必须至少有一个元素! - leftaroundabout
这个回答非常有道理!谢谢。 - Tarik
@leftaroundabout,XOR确实有一个恒等元素。对于所有的b值,xor False b == b - Lambda Fairy
显示剩余2条评论

7
优秀的答案,但我认为提供更直观的处理方式是值得的。然而,不要使用 and :: [Bool] -> Bool,我们来看一下 all :: (a -> Bool) -> [Bool] -> Bool。你可以这样想 all。将谓词(a -> Bool 参数)想象成关于列表元素的一个假设。然后,all仅在列表包含至少一个与假设相反的实例时返回False。如果列表为空,则没有反例,因此它是显然正确的。
回到and,请注意andall是可互换的。如果你有and,你可以这样定义all
all :: (a -> Bool) -> [a] -> Bool
all pred = and . map pred

反之亦然,如果你已经有了all,你可以从中定义and

and :: [Bool] -> Bool
and = all id

5
除了@bheklilr的答案之外,我们需要回想一下Monoid是一个元组 (M,e,<>),其中M是一个对象(可以将其视为类型),e是对象M的一个点(e:M - 类型元素),<>是一个二进制操作符,它是可结合的,并且具有e作为标识:
<> : M -> M -> M
e <> x = x
x <> e = x
(x <> y) <> z = x <> (y <> z)

在一些单子群之间存在单子群同态。有一个自由单子群 - 从中可以映射到任何其他单子群的单子群。这样的自由单子群是一个列表:([a], [], ++),可以映射到任何其他单子群。例如:

([Int], [], ++) => (Int, 0, +)
([Int], [], ++) => (Int, 1, *)
([Bool], [], ++) => (Bool, True, &&)
([Bool], [], ++) => (Bool, False, ||)

然后,sumproductandor分别是相应的幺半群同态,映射类型[Int][Bool]的元素。根据幺半群同态的定义,幺半群的映射h被执行时,任何列表x++y都被映射到点h(x ++ y) == (h x) <> (h y) - 例如,and (x++[]) == (and x) && (and [])。从后面的例子可以清楚地看出,由于x++[] == x,所以(and x) && (and []) == and x,因此,and []被映射到(Bool, True, &&)的恒等元素。

@dfeuer 中的“对象指针”是范畴论术语。你可以大致说它是一个集合的元素,或者是一个类型的实例。 - Sassa NF
我对范畴论中的“对象”有一定的了解,但对其中的“点”不太理解。 - dfeuer

4
一种思考 TrueFalse 的方式是将它们视为有序的 lattice 元素,按照 False < True 排序。 &&|| 可以被看作是该 lattice 的二元“meet”(最大下界)和“join”(最小上界)运算。同样地,andor 是一般的有限 meet 和有限 join 运算。那么 and [] 是什么呢?它是 [] 的最大下界。但是 True(不真实的情况下)小于或等于 [] 的每个元素,因此它是 [] 的一个下界,并且(当然)大于任何其他下界(另一个是 False),因此 and [] = True。代数观点(从单子和诸如此类的角度思考)事实上完全等同于 order-theoretic 观点,但我认为 order-theoretic 观点提供了更多的直觉。

2

and 的逻辑是在列表中查找第一个值为 False 的条目。如果没有找到该条目,则结果为 True。例如:

and $ map even [2..]

不会遍历整个无限列表,而是在 3 处停止并返回 False。空列表中没有 False 元素,因此我们默认为 True

对于 or 也是类似的:它尝试找到第一个 True 然后停止,否则就是 False


1

and 的意思是:“所有的东西都是 True 吗?” 当它为空时,里面的所有东西(不多)都是真的,所以是肯定的 (True)。

or 的意思是:“有任何一个是 True 吗?” 当没有任何东西时,那里没有任何真实的东西。 (False)


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