Why “and []” is True and “or []” is False

2020-02-01 04:16发布

Why "and" on an empty list returns true, does it imply that an empty list holds True? Sorry but I cannot read and comprehend this correctly, so please correct me. Thanks.

Prelude> and []
True
Prelude> or []
False

7条回答
Animai°情兽
2楼-- · 2020-02-01 04:45

In addition to @bheklilr answer, let's recall that a Monoid is a tuple (M,e,<>), where M is a object (you can think of it as a type), e is a point of the object M (e : M - element of type) and <> is a binary operation, which is associative and has e as identity:

<> : M -> M -> M
e <> x = x
x <> e = x
(x <> y) <> z = x <> (y <> z)

There are monoid homomorphisms between some monoids. There is one free monoid - the monoid from which there is a homomorphism into any other. Such free monoid is a list: ([a], [], ++) can be mapped into any other monoid. For example:

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

Then sum, product, and, or are the respective monoid homomorphisms, mapping the elements of the types [Int] and [Bool]. By the definition of the monoid homomorphism, the mapping h of the monoids is performed in such a way that any list x++y is mapped into the point h(x ++ y) == (h x) <> (h y) - for example, and (x++[]) == (and x) && (and []). It becomes clear from the latter example, that since x++[] == x, so (and x) && (and []) == and x, therefore, and [] maps into the identity element of (Bool, True, &&).

查看更多
倾城 Initia
3楼-- · 2020-02-01 04:46

and means: "Is everything there True?". When it's empty, everything's that is in there (which isn't much) is true, so that's a yes (True).

or means: "Is anything there True?". When there's nothing there, there's nothing true there. (False)

查看更多
Juvenile、少年°
4楼-- · 2020-02-01 04:48

In mathematics, it's often useful to talk about a binary operation, such as &&, ||, +, *, etc as having an identity. The identity is a value e such that the following property holds for some generic binary operation <>

e <> x = x
x <> e = x

For the operators I listed above, they are commutative, meaning that x <> y = y <> x for all x and y, so we only have to check one of the above properties. For and, the binary operator in question is &&, and for or the binary operator is ||. If we make a Cayley table for these operations, it would look like

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


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

So as you can see, for && if you have True && False and True && True, the answer is always the second argument to &&. For ||, if you have False || False and False || True, the answer is always the second argument, so the first argument of each must be the identity element under those operators. Put simply:

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

False || x = x
x || False = x

Thus, the preferred answer when there are no elements to perform the operator on is the identity element for each operation.


It might help to also think about the identity elements for + and *, which are 0 and 1 respectively:

x + 0 = x = 0 + x
x * 1 = x = 1 * x

You can also extend this to operations like list concatenation (++ with []), function composition for functions of type a -> a ((.) with id), along with many others. Since this is starting to look like a pattern, you might ask if this is already a thing in Haskell, and indeed it is. The module Data.Monoid defines the Monoid typeclass that abstracts this pattern, and it's minimal definition is

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

And it even aliases mappend as <> for ease of use (it was no accident that I choose it above for a generic binary operator). I encourage you to look at that module and play around with its definitions. The source code is quite easy to read and is enlightening.

查看更多
Bombasti
5楼-- · 2020-02-01 04:48

and and or are just folds, and a fold called on an empty list will produce its starting argument, which is True or False, respectively.

They are implemented using a fold only if Prelude is loaded, otherwise they are realised using explicit recursion, which in itself still is a fold despite not actually making use of foldr or foldl. They still behave the same as we can see by examining the source:

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

Here is a link to the implementations.


To clear the confusion in the comments: A fold is a function which takes a binary function and a starting value (often called accumulator) and traverses a list until it is empty. When called on an empty list, the fold will return the accumulator as is where it does not matter if the list has already been traversed or not. This is a sample implementation of foldr:

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

and is simply

and = foldr (&&) True

which makes and [] evaluate to True.

查看更多
Anthone
6楼-- · 2020-02-01 04:57

The logic of and is to find the first entry in the list which is False. If the entry is not found, the result is True. For example:

and $ map even [2..]

will not iterate through the whole infinite list, but will stop at 3 and return False. There is no False element in the empty list, so we default to True.

For or it's similarly: it tries to find the first True and then stops, otherwise it's False.

查看更多
爱情/是我丢掉的垃圾
7楼-- · 2020-02-01 05:01

Excellent answers, but I think it's worth providing a more intuitive treatment. Instead of and :: [Bool] -> Bool, however, let's look at all :: (a -> Bool) -> [Bool] -> Bool. You can think of all this way. Picture the predicate (the a -> Bool argument) as a hypothesis about list elements. Then all returns False if and only if the list contains at least one counterexample to the hypothesis. If the list is empty there are no counterexamples, so it's trivially confirmed.

To bring it back to and, note that and and all are interdefinable. If you have and, you can define all this way:

all :: (a -> Bool) -> [a] -> Bool
all pred = and . map pred

And vice-versa, if you already had all, you could define and from it:

and :: [Bool] -> Bool
and = all id
查看更多
登录 后发表回答