为什么空列表上的"and" 运算返回True,是否意味着空列表为True?对不起,我无法正确理解这个问题,请纠正我。谢谢。
Prelude> and []
True
Prelude> or []
False
Prelude> and []
True
Prelude> or []
False
&&
、||
、+
、*
等)具有一个称为恒等元素的价值是很有用的。 恒等元素是一个值e
,使得下面的性质对于一些通用的二元运算<>
成立。e <> x = x
x <> e = x
我列出的运算符是可交换的,也就是说对于所有的x
和y
, x <> y = y <> x
,因此我们只需要检查上述属性之一。 对于and
运算,所涉及的二元运算符是&&
,而对于or
运算,其二元运算符为||
。如果我们为这些操作制作Cayley表格,它会看起来像:
&& | False | True
------+-------+------
False | False | False
True | False | True
|| | False | True
------+-------+------
False | False | True
True | True | True
正如您所看到的,对于 &&
,如果您有 True && False
和 True && True
,答案始终是 &&
的第二个参数。 对于 ||
,如果您有 False || False
和 False || True
,答案始终是第二个参数,因此每个参数的第一个参数必须是这些运算符下的恒等元素。 简而言之:
True && x = x
x && True = x
False || x = x
x || False = x
因此,当没有元素可执行操作时,每个运算的首选答案是恒等元素。
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
别名为<>
以便使用(选择这个名称作为通用二元运算符并非偶然)。我鼓励您查看该模块并尝试使用其定义。源代码非常易于阅读,可以启发人。
and
和or
只是折叠函数,对于一个空列表调用折叠函数会生成其起始参数,分别是True
或False
。
如果加载了Prelude
,它们将使用折叠函数来实现。否则,它们将使用显式递归来实现,即使不使用foldr
或foldl
,仍然可以将其视为折叠函数。通过查看源代码,我们可以看到它们的行为仍然相同:
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
.
xor :: [Bool] -> Bool
。然而,有 xor :: NonEmpty Bool -> Bool
,它不需要身份元素,因为列表中必须至少有一个元素! - leftaroundaboutb
值,xor False b == b
。 - Lambda Fairyand :: [Bool] -> Bool
,我们来看一下 all :: (a -> Bool) -> [Bool] -> Bool
。你可以这样想 all
。将谓词(a -> Bool
参数)想象成关于列表元素的一个假设。然后,all
仅在列表包含至少一个与假设相反的实例时返回False
。如果列表为空,则没有反例,因此它是显然正确的。and
,请注意and
和all
是可互换的。如果你有and
,你可以这样定义all
:all :: (a -> Bool) -> [a] -> Bool
all pred = and . map pred
反之亦然,如果你已经有了all
,你可以从中定义and
:
and :: [Bool] -> Bool
and = all id
(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, ||)
sum
、product
、and
、or
分别是相应的幺半群同态,映射类型[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, &&)
的恒等元素。True
和 False
的方式是将它们视为有序的 lattice 元素,按照 False < True
排序。 &&
和 ||
可以被看作是该 lattice 的二元“meet”(最大下界)和“join”(最小上界)运算。同样地,and
和 or
是一般的有限 meet 和有限 join 运算。那么 and []
是什么呢?它是 []
的最大下界。但是 True
(不真实的情况下)小于或等于 []
的每个元素,因此它是 []
的一个下界,并且(当然)大于任何其他下界(另一个是 False
),因此 and [] = True
。代数观点(从单子和诸如此类的角度思考)事实上完全等同于 order-theoretic 观点,但我认为 order-theoretic 观点提供了更多的直觉。and
的逻辑是在列表中查找第一个值为 False
的条目。如果没有找到该条目,则结果为 True
。例如:
and $ map even [2..]
不会遍历整个无限列表,而是在 3
处停止并返回 False
。空列表中没有 False
元素,因此我们默认为 True
。
对于 or
也是类似的:它尝试找到第一个 True
然后停止,否则就是 False
。
and
的意思是:“所有的东西都是 True
吗?” 当它为空时,里面的所有东西(不多)都是真的,所以是肯定的 (True
)。
or
的意思是:“有任何一个是 True
吗?” 当没有任何东西时,那里没有任何真实的东西。 (False
)