Using any() and all() as reductions
The any() and all() functions provide boolean reduction capabilities. Both functions reduce a collection of values to a single True or False. The all() function ensures that all values are True. The any() function ensures that at least one value is True.
These functions are closely related to a universal quantifier and an existential quantifier used to express mathematical logic. We may, for example, want to assert that all elements in a given collection have a property. One formalism for this could look like the following:
We read this as for all x in S, the function, Prime(x), is true. We've put a quantifier, for all, in front of the logical expression.
In Python we switch the order of the items slightly to transcribe the logic expression as follows:
all(isprime(x) for x in someset)
This will evaluate the isprime(x) function for each distinct value of x and reduce the collection of values to a single True or False.
The any() function is related to the existential quantifier. If we want to assert that no value in a collection is prime, we could use one of these two equivalent expressions:
The first states that it is not the case that all elements in S are prime. The second version asserts that there exists one element in S that is not prime. These two are equivalent, that is if not all elements are prime, then one element must be non-prime.
In Python, we can switch the order of the terms and transcribe these to working code as follows:
not all(isprime(x) for x in someset) any(not isprime(x) for x in someset)
As they're equivalent, there are two reasons for preferring one over the other: performance and clarity. The performance is nearly identical, so it boils down to clarity. Which of these states the condition the most clearly?
The all() function can be described as an and reduction of a set of values. The result is similar to folding the and operator between the given sequence of values. The any() function, similarly, can be described as an or reduction. We'll return to this kind of general purpose reducing when we look at the reduce() function in Chapter 10, The Functools Module. There's no best answer here; it's a question of what seems most readable to the intended audience.
We also need to look at the degenerate case of these functions. What if the sequence has no elements? What are the values of all(()) or all([])?
If we ask, "Are all the elements in an empty set prime? Then what's the answer? For guidance on this, we'll expand the question slightly, and look at the idea of an identity element.
If we ask, "Are all the elements in an empty set prime, and are all the elements in SomeSet prime?" we have a hint as to how we have to proceed. We're performing an and reduction of an empty set and an and reduction of SomeSet:
It turns out that the and operator can be distributed freely. We can rewrite this to, as a union of the two sets, which is then evaluated for being prime:
Clearly, . If we union a set, S, with an empty set, we get the original set, S. The empty set can be called the union identify element. This is parallel to the way zero is the additive identity element:
Similarly, any(()) must be the or identity element, which is False. If we think of the multiplicative identify element, 1, where , then all(()) must be True.
The following code demonstrates that Python follows these rules:
>>> all(()) True >>> any(()) False
Python gives us some very nice tools to perform processing that involves logic. We have the built-in and, or, and not operators. However, we also have these collection-oriented any() and all() functions.