Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Python > Pythonic function composition

Reply
Thread Tools

Pythonic function composition

 
 
Alan G Isaac
Guest
Posts: n/a
 
      10-25-2004
Given a list of functions, it seems there must be a
Pythonic approach to composition. Something like

def compose(fns): return lambda x: reduce(lambda f,g: f(g),fns)(x)

This will not work because the argument 'x' is not "inside".
What is the proper formulation?

Thanks,
Alan Isaac


 
Reply With Quote
 
 
 
 
Peter Otten
Guest
Posts: n/a
 
      10-25-2004
Alan G Isaac wrote:

> Given a list of functions, it seems there must be a
> Pythonic approach to composition. Something like
>
> def compose(fns): return lambda x: reduce(lambda f,g: f(g),fns)(x)
>
> This will not work because the argument 'x' is not "inside".
> What is the proper formulation?


You need to pass a function that makes a function (a "factory") to reduce():

>>> fns = [lambda x: x+2, lambda x: x*2, lambda x: x*x]
>>> def compose(f, g):

.... def fog(x):
.... return f(g(x))
.... return fog
....
>>> g1 = reduce(compose, fns)
>>> g1(2)

10

The same with lambdas:

>>> g2 = reduce(lambda f, g: lambda x: f(g(x)), fns)
>>> g2(2)

10

Peter

 
Reply With Quote
 
 
 
 
Oliver Fromme
Guest
Posts: n/a
 
      10-25-2004
Alan G Isaac <(E-Mail Removed)> wrote:
> Given a list of functions, it seems there must be a
> Pythonic approach to composition. Something like
>
> def compose(fns): return lambda x: reduce(lambda f,g: f(g),fns)(x)
>
> This will not work because the argument 'x' is not "inside".
> What is the proper formulation?


There are probably several ways to do it.
The following is the one which come to my mind first.

The trick is to first define a function that composes
two functions, and then use reduce() to apply it to an
arbitrary number of functions.

>>> def compose2 (f, g):

.... def h (x):
.... return f(g(x))
.... return h
....
>>> def compose (fns):

.... return reduce(compose2, fns)
....

Some testing:

>>> def add42 (x): return x + 42

....
>>> def mul2 (x): return x * 2

....
>>> def sub5 (x): return x - 5

....
>>> def div3 (x): return x / 3

....
>>> a = compose((add42, mul2, sub5, div3))
>>> a(1)

27

There's no need to juggle with lambda in this case.
Lambda has its uses, but this isn't one of them.

Best regards
Oliver

--
Oliver Fromme, Konrad-Celtis-Str. 72, 81369 Munich, Germany

``All that we see or seem is just a dream within a dream.''
(E. A. Poe)
 
Reply With Quote
 
Alan G Isaac
Guest
Posts: n/a
 
      10-25-2004

"Peter Otten" <(E-Mail Removed)> wrote in message
news:clj5nq$jl$02$(E-Mail Removed)-online.com...
> >>> g2 = reduce(lambda f, g: lambda x: f(g(x)), fns)



Cool. That gets me there. I think
def compose(fns) : return reduce(lambda f, g: lambda x: f(g(x)), fns)
does exactly what I want.

Thanks,
Alan Isaac



 
Reply With Quote
 
Alan G Isaac
Guest
Posts: n/a
 
      10-25-2004
"Oliver Fromme" <(E-Mail Removed)> wrote in message
news:(E-Mail Removed)...
> There's no need to juggle with lambda in this case.
> Lambda has its uses, but this isn't one of them.


I kind of like the lambda version (see Peter's post).
But maybe it is more opaque.

Thanks,
Alan


 
Reply With Quote
 
Michael J. Fromberger
Guest
Posts: n/a
 
      10-25-2004
In article <(E-Mail Removed)>,
"Alan G Isaac" <(E-Mail Removed)> wrote:

> Given a list of functions, it seems there must be a
> Pythonic approach to composition. Something like
>
> def compose(fns): return lambda x: reduce(lambda f,g: f(g),fns)(x)
>
> This will not work because the argument 'x' is not "inside".
> What is the proper formulation?
>


If you are only concerned with unary functions, then composition is
fairly trivial to deal with:

def compose(*fns):
def id(x): return x

def c2(f, g):
def h(x): return f(g(x))
return h

return reduce(c2, fns, id)

However, if you want to deal with functions that may take multiple
arguments, you must be a little more clever. Here's one way that seems
to work okay:

def compose(*fns):
def id(*args): return args

def box(res):
if isinstance(res, (list, tuple)):
return res
else:
return (res,)

def unbox(res):
if isinstance(res, (list, tuple)) and len(res) == 1:
return res[0]
else:
return res

def c2(f, g):
def h(*args):
return unbox(f(*box(g(*args))))
return h

return reduce(c2, fns, id)

For instance:
def f1(a, b):
return (a / b, a % b)

def f2(a, b):
return a + b

def f3(a):
return a + 2

h = compose(f3, f2, f1)
h(5, 3)
==> 5

This will work, but it's not the most efficient possible solution. You
could defer unboxing until the end by defining another intermediate
function.

Cheers,
-M

--
Michael J. Fromberger | Lecturer, Dept. of Computer Science
http://www.dartmouth.edu/~sting/ | Dartmouth College, Hanover, NH, USA
 
Reply With Quote
 
Lonnie Princehouse
Guest
Posts: n/a
 
      10-25-2004
How about some recursion?

def compose(f, *fns):
if not fns:
return f
else:
return lambda x: f(compose(*fns)(x))

....

>>> from math import *
>>> foo = compose(sin, sqrt, abs) # sin(sqrt(abs(x)))
>>> foo(-((pi/2.)**2))

1.0

....or you could try it this way, which makes some assumptions about
function names, but will possibly run faster:

def compose(*fns):
fnames = [f.__name__ for f in fns]
expr = "%s(x%s" % ('('.join(fnames),')'*len(fns))
return eval("lambda x: %s" % expr)
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
An idea for fast function composition Arnaud Delobelle Python 4 02-17-2008 12:07 AM
Python feature request : operator for function composition Kay Schluehr Python 10 02-04-2008 05:08 PM
Function composition in Ruby Tom Moertel Ruby 2 04-07-2006 07:10 PM
Object Oriented vs Pythonic Code, and Pythonic standards Carl J. Van Arsdall Python 4 02-07-2006 10:15 PM
Defining a new function by composition Edgardo Hames Ruby 7 08-07-2004 09:31 AM



Advertisments