为什么"和[]"为真,"或[]"为假

Roh*_*rma 10 haskell monoids

为什么空列表中的"和"返回true,是否表示空列表为True?对不起,但我无法正确阅读和理解,所以请纠正我.谢谢.

Prelude> and []
True
Prelude> or []
False
Run Code Online (Sandbox Code Playgroud)

bhe*_*ilr 38

在数学上,它往往是有用谈论二元运算,例如&&,||,+,*,等为具有身份.标识是一个值e,使得以下属性适用于某些通用二进制操作<>

e <> x = x
x <> e = x
Run Code Online (Sandbox Code Playgroud)

对于我上面列出的运营商,他们是可交换的,这意味着x <> y = y <> x所有的xy,所以我们只需要检查上面的属性之一.因为and,有问题的二元运算符是&&,对于or二元运算符是||.如果我们为这些操作制作Cayley表,它看起来就像

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


||    | False | True
------+-------+------
False | False | True
True  | True  | True
Run Code Online (Sandbox Code Playgroud)

因此,大家可以看到,对于&&如果您有True && FalseTrue && True,答案永远是第二个参数&&.对于||,如果您有False || FalseFalse || True,答案永远是第二个参数,所以每次的第一个参数必须是那些经营者根据单位元.简单地说:

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

False || x = x
x || False = x
Run Code Online (Sandbox Code Playgroud)

因此,当没有要执行操作符的元素时的优选答案是每个操作的标识元素.


这可能有助于想想也是身份的元素+*,这是01分别为:

x + 0 = x = 0 + x
x * 1 = x = 1 * x
Run Code Online (Sandbox Code Playgroud)

您还可以将此扩展到列表连接(++with []),类型a -> a((.)with id)函数的函数组合以及许多其他操作.由于这开始看起来像一个模式,你可能会问这是否已经是Haskell中的一个东西,事实确实如此.该模块Data.Monoid定义了Monoid抽象此模式的类型类,它的最小定义是

class Monoid a where
    mempty :: a                   -- The identity
    mappend :: a -> a -> a        -- The binary operator
Run Code Online (Sandbox Code Playgroud)

它甚至mappend<>易于使用的别名(我在上面为一般二元运算符选择它并不是偶然的).我鼓励您查看该模块并使用其定义.源代码非常易于阅读并具有启发性.


Thr*_*eFx 12

and并且or只是折叠,并且在空列表上调用的折叠将产生其起始参数,分别是TrueFalse.

它们只有在Prelude加载时才使用折叠实现,否则它们是使用显式递归实现的,尽管实际上没有使用foldr或者仍然是折叠foldl.通过检查来源,它们的行为仍然与我们看到的相同:

and [] = True
and (x:xs) = x && and xs
or [] = False
or (x:xs) = x || or xs
Run Code Online (Sandbox Code Playgroud)

是实现的链接.


清除注释中的混淆:fold是一个函数,它接受二进制函数和起始值(通常称为累加器)并遍历列表直到它为空.当在空列表上调用时,折叠将返回累加器,如果列表已经遍历则无关紧要.这是一个示例实现foldr:

foldr _ acc [] = acc
foldr f acc (x:xs) = f x (foldr f acc xs)
Run Code Online (Sandbox Code Playgroud)

and 很简单

and = foldr (&&) True
Run Code Online (Sandbox Code Playgroud)

这使得and []评估True.


Lui*_*las 7

优秀的答案,但我认为值得提供更直观的治疗.相反的and :: [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
Run Code Online (Sandbox Code Playgroud)

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

and :: [Bool] -> Bool
and = all id
Run Code Online (Sandbox Code Playgroud)


Sas*_* NF 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)
Run Code Online (Sandbox Code Playgroud)

一些幺半群之间存在幺半群同态.有一个自由的幺半群 - 与其他任何一个同态的幺半群.这样的免费monoid是一个列表:([a], [], ++)可以映射到任何其他monoid.例如:

([Int], [], ++) => (Int, 0, +)
([Int], [], ++) => (Int, 1, *)
([Bool], [], ++) => (Bool, True, &&)
([Bool], [], ++) => (Bool, False, ||)
Run Code Online (Sandbox Code Playgroud)

然后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, &&).