Simplify explanation of multiset operations by removing restrictions on negative inputs.

This commit is contained in:
Raymond Hettinger 2009-01-21 20:36:27 +00:00
parent 63b3a97a2a
commit e0d1b9f11f
3 changed files with 17 additions and 24 deletions

View File

@ -253,16 +253,18 @@ Common patterns for working with :class:`Counter` objects::
c.items() # convert to a list of (elem, cnt) pairs
Counter(dict(list_of_pairs)) # convert from a list of (elem, cnt) pairs
c.most_common()[:-n:-1] # n least common elements
c += Counter() # remove zero and negative counts
Several multiset mathematical operations are provided for combining
:class:`Counter` objects. Multisets are like regular sets but allowed to
:class:`Counter` objects. Multisets are like regular sets but are allowed to
contain repeated elements (with counts of one or more). Addition and
subtraction combine counters by adding or subtracting the counts of
corresponding elements. Intersection and union return the minimum and maximum
of corresponding counts::
of corresponding counts. All four multiset operations exclude results with
zero or negative counts::
>>> c = Counter({'a': 3, 'b': 1})
>>> d = Counter({'a': 1, 'b': 2})
>>> c = Counter(a=3, b=1)
>>> d = Counter(a=1, b=2)
>>> c + d # add two counters together: c[x] + d[x]
Counter({'a': 4, 'b': 3})
>>> c - d # subtract (keeping only positive counts)
@ -272,16 +274,6 @@ of corresponding counts::
>>> c | d # union: max(c[x], d[x])
Counter({'a': 3, 'b': 2})
All four multiset operations produce only positive counts (negative and zero
results are skipped). If inputs include negative counts, addition will sum
both counts and then exclude non-positive results. The other three operations
are undefined for negative inputs::
>>> e = Counter(a=8, b=-2, c=0)
>>> e += Counter() # remove zero and negative counts
>>> e
Counter({'a': 8})
.. seealso::
* `Bag class <http://www.gnu.org/software/smalltalk/manual-base/html_node/Bag.html>`_

View File

@ -314,8 +314,8 @@ class Counter(dict):
if not isinstance(other, Counter):
return NotImplemented
result = Counter()
for elem, count in self.items():
newcount = count - other[elem]
for elem in set(self) | set(other):
newcount = self[elem] - other[elem]
if newcount > 0:
result[elem] = newcount
return result

View File

@ -462,18 +462,19 @@ class TestCounter(unittest.TestCase):
for i in range(1000):
# test random pairs of multisets
p = Counter(dict((elem, randrange(-2,4)) for elem in elements))
p.update(e=1, f=-1, g=0)
q = Counter(dict((elem, randrange(-2,4)) for elem in elements))
for counterop, numberop, defneg in [
(Counter.__add__, lambda x, y: x+y if x+y>0 else 0, True),
(Counter.__sub__, lambda x, y: x-y if x-y>0 else 0, False),
(Counter.__or__, max, False),
(Counter.__and__, min, False),
q.update(h=1, i=-1, j=0)
for counterop, numberop in [
(Counter.__add__, lambda x, y: max(0, x+y)),
(Counter.__sub__, lambda x, y: max(0, x-y)),
(Counter.__or__, lambda x, y: max(0,x,y)),
(Counter.__and__, lambda x, y: max(0, min(x,y))),
]:
result = counterop(p, q)
for x in elements:
# all except __add__ are undefined for negative inputs
if defneg or (p[x] >= 0 and q[x] >= 0):
self.assertEqual(numberop(p[x], q[x]), result[x])
self.assertEqual(numberop(p[x], q[x]), result[x],
(counterop, x, p, q))
# verify that results exclude non-positive counts
self.assert_(x>0 for x in result.values())