* Remove claim to minimize use of reduce()

* Add claim to minimize use of lambda.
* Remove details of proposed new accumulator functions.
* Reference Jeff Epler's proof-of-concept patch.
* Minor wording improvements everywhere.
This commit is contained in:
Raymond Hettinger 2003-10-22 22:08:06 +00:00
parent a08117dee0
commit 1a2ad824e4
1 changed files with 25 additions and 101 deletions

View File

@ -43,15 +43,21 @@ Similar benefits are conferred on constructors for container objects::
s = Set(word for line in page for word in line.split())
d = dict( (k, func(v)) for k in keylist)
Generator expressions are especially useful in functions that reduce
an iterable input to a single value::
Generator expressions are especially useful with functions like sum(),
min(), and max() that reduce an iterable input to a single value::
sum(len(line) for line in file if line.strip())
max(len(line) for line in file if line.strip())
Accordingly, generator expressions are expected to partially eliminate
the need for reduce() which is notorious for its lack of clarity. And,
there are additional speed and clarity benefits from writing expressions
directly instead of using lambda.
Generator expressions also address some examples of functionals coded
with lambda::
reduce(lambda s, a: s + a.myattr, data, 0)
reduce(lambda s, a: s + a[3], data, 0)
These simplify to::
sum(a.myattr for a in data)
sum(a[3] for a in data)
List comprehensions greatly reduced the need for filter() and map().
Likewise, generator expressions are expected to minimize the need
@ -76,13 +82,13 @@ its present form. The impetus for the discussion was an innovative
proposal from Peter Norvig [3]_.
The Gory Details
================
The Details
===========
1. The semantics of a generator expression are equivalent to creating
an anonymous generator function and calling it. There's still discussion
about whether that generator function should copy the current value of all
free variables into default arguments.
about whether that generator function should copy the current value of
all free variables into default arguments.
2. The syntax requires that a generator expression always needs to be inside
a set of parentheses and cannot have a comma on either side. Unfortunately,
@ -135,95 +141,10 @@ Reduction Functions
===================
The utility of generator expressions is greatly enhanced when combined
with appropriate reduction functions like sum(), min(), and max(). I
propose creating a set of high speed reduction functions designed to tap the
power of generator expressions and replace the most common uses of reduce()::
def xorsum(it):
return reduce(operator.xor, it, 0)
def product(it):
return reduce(operator.mul, it, 1)
def anytrue(it):
for elem in it:
if it:
return True
return False
def alltrue(it):
for elem in it:
if it:
return False
return True
def horner(it, x):
'horner([6,3,4], 5) evaluates 6*x**2 + 3*x + 4 at x=5'
cum = 0.0
for c in it:
cum = cum*x + c
return cum
def mean(it):
data = list(it)
return sum(data) / len(data)
def smallest(it, siz=1):
result = []
for elem in it:
if len(result) < siz:
bisect.insort_left(result, elem)
elif elem < result[-1]:
result.pop()
bisect.insort_left(result, elem)
return result
def largest(it, siz=1):
result = []
for elem in it:
if len(result) < siz:
bisect.insort_left(result, elem)
elif elem > result[0]:
result.pop(0)
bisect.insort_left(result, elem)
result.reverse()
return result
Notes on reduce()
=================
Reduce typically has three types of use cases:
1) Common reduction functions applied directly to elements in a sequence.
This use case is addressed by the sum(), min(), max(), and the additional
functions listed above.
2) Reduce is often used with lambda when the data needs be extracted from
complex sequence elements. For example::
reduce(lambda sum, x: sum + x.myattr, data, 0)
reduce(lambda prod, x: prod * x[3], data, 1)
In concert with reduction functions, generator expressions completely
fulfill these use cases::
sum(x.myattr for x in data)
product(x[3] for x in data)
3) On rare occasions, the reduction function is non-standard and requires
custom coding::
reduce(lambda cum, c: (cum >> 8) ^ crc32[ord(a) ^ (cum & 0x00ff)], data, -1)
Because a complex lambda is required, this use case becomes clearer and
faster when coded directly as a for-loop::
cum = -1
for c in data:
cum = (cum >> 8) ^ crc32[ord(a) ^ (cum & 0x00ff)]
In conclusion, after adding generator expressions and a set of common reduction
functions, few, if any cases remain for reduce().
with reduction functions like sum(), min(), and max(). Separate
proposals are forthcoming that recommend several new accumulation
functions possibly including: product(), average(), alltrue(),
anytrue(), nlargest(), nsmallest().
Acknowledgements
@ -258,7 +179,10 @@ References
.. [3] Peter Norvig's Accumulation Display Proposal
http:///www.norvig.com/pyacc.html
.. [4] Jeff Epler had worked up a patch demonstrating
the previously proposed bracket and yield syntax
http://python.org/sf/795947
Copyright
=========