Welcome, guest | Sign In | My Account | Store | Cart

reduce is a pretty nice built-in, but its usage in everyday code is seemingly rare. If you've been looking for excuses to use reduce, here are a few useful recipes to get you started.

Python, 42 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import operator

def factorial(n):
    """Calculate n factorial"""
    return reduce(operator.mul, range(2, n+1), 1)

def intersection(*sets):
    """Get the intersection of all input sets"""
    return reduce(set.intersection, sets)

def union(*sets):
    """Get the union of all input sets"""
    return reduce(set.union, sets)

def join(*seqs):
    """Join any input sequences that support concatenation"""
    return reduce(operator.concat, seqs)

"""
Some usage:
    
>>> factorial(3)
6

>>> factorial(10)
3628800

>>> a = set([1, 2, 3, 4, 5])
>>> b = set([5, 6, 3, 7])
>>> c = set([8, 7, 5])
>>> intersection(a, b, c)
set([5])

>>> union(a, b, c)
set([1, 2, 3, 4, 5, 6, 7, 8])

>>> join("one", "two", "three", "four")
'onetwothreefour'

>>> join([1, 2, 3], [5, 6], [6, 7])
[1, 2, 3, 4, 5, 6, 7]
"""

If you post a clever way to use reduce to solve a common problem, post it here and I'll add it to the recipe.

4 comments

Brandon Beck 15 years, 8 months ago  # | flag

Chained replacements in a string. One way that I've recently found to use reduce is for the chained replacement of characters in a string.

So instead of:

def replace(text, replacements):
    for (old, new) in replacements:
        text = text.replace(old, new)

    return text

I use:

def replace(text, replacements):
    return reduce(lambda s, (old, new): s.replace(old, new), replacements, text)

And an example:

replace("This is a normal string", [(c, c.upper()) for c in "aeiou"])
'ThIs Is A nOrmAl strIng'

Ironically enough, on my machine the the reduce version is about 37% slower than the looped version on the example I gave above.

Frank P Mora 15 years, 7 months ago  # | flag

Anonymous in-line list comprehension functions. Python is a wonderfully expressive programming language. Some expressions such as those involving reduce, are more elegant and simple than others. I enjoy and value the functional facilities and functions as first class objects in Python. I have, however, seen evidence that some of these are in jeopardy (google for “The fate of reduce() in Python”.)

Here are in-line list comprehension versions of your functions and a bit more. The general form is: set initial value, set iteration values and successively apply an infix or prefix function cumulatively and finally take the last value of the generated list.

>>> T=[set(s) for s in [(1,2,3,4,5),(2,3,4,5,6),(3,4,5,6,7),(4,5,6,7,8)]]

>>> f=set.intersection
>>> [ s for s in [f(*T[:2])] for i in range(2,len(T)) for s in [f(s,T[i])] ][-1]
set([4, 5])     ## issues the null set if no intersection between all given sets

>>> f=set.union
>>> [ s for s in [f(*T[:2])] for i in range(2,len(T)) for s in [f(s,T[i])] ][-1]
set([1, 2, 3, 4, 5, 6, 7, 8])

>>> # join
>>> T=[(1,2,3,4,5,6,7),(3,4,5,6,7),(5,6,7),(7,8,9,2,1,5)]

>>> [ s for s in [T[0]] for i in range(1,len(T)) for s in [s + T[i]] ][-1]
(1, 2, 3, 4, 5, 6, 7, 3, 4, 5, 6, 7, 5, 6, 7, 7, 8, 9, 2, 1, 5)

>>> t=["abcd","efgh","ijkl","mnop","qrst"]

>>> [ s for s in [T[0]] for i in range(1,len(T)) for s in [s + T[i]] ][-1]
'abcdefghijklmnopqrst'

>>> # factorial
>>> fac = 6
>>> [j for j in [1] for i in range(2, fac+1) for j in [j*i]][-1]
720

>>> nl = [2, 3, 6, 18]

>>> # sum
>>> [j for j in [0] for i in nl for j in [j + i]][-1]
29
>>> # product
>>> [j for j in [1] for i in nl for j in [j * i]][-1]
648
>>>

Much thanks to Steven Bethard for the cleaner form of this pattern.
Frank P Mora 15 years, 7 months ago  # | flag

And for predicates.

>>> l=(20,30,40,50,60,70,80,90,1,2,3,4,5)

>>> # all function
>>> [ p for p in [l[0]>42] for i in range(1,len(l)) for p in [p and l[i]>42] ][-1]
False

>>> # any function
>>> [ p for p in [l[0]>42] for i in range(1,len(l)) for p in [p  or l[i]>42] ][-1]
True

The pattern *is* very general and regular
Paul McGuire 14 years, 7 months ago  # | flag

Use reduce to call divmod with succesive divisors. I used reduce to build up a tuple of the parts of an elapsed time, starting with a floating point number of seconds. Posted at recipe: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/511486

-- Paul

Created by Brian Beck on Fri, 31 Mar 2006 (PSF)
Python recipes (4591)
Brian Beck's recipes (5)

Required Modules

Other Information and Tasks