* 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:
parent
a08117dee0
commit
1a2ad824e4
126
pep-0289.txt
126
pep-0289.txt
|
@ -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
|
||||
=========
|
||||
|
|
Loading…
Reference in New Issue