Functional Programming

Bengt Richter bokr at oz.net
Mon Dec 30 10:39:18 EST 2002


On 29 Dec 2002 21:51:24 -0600, Mike Meyer <mwm at mired.org> wrote:

>"Martin v. Löwis" <martin at v.loewis.de> writes:
>
>> beno wrote:
>> > Does anyone know of good sources of info for programming using the
>> > principles of functional programming in Python? Or, to what
>> > languages is Haskell similar? Any other advice on the subject
>> > equally welcomed!
>> - Currying (creating a function from another function by passing some
>>    arguments): This is not directly available in Python; you can often
>>    emulate it with lambda expressions.
>
>You don't need lambdas, and you can even make the programmer API look
>the same. For example:
>
>>>> inc = curry(int.__add__, 1)
>>>> inc(3)
>4
>>>> inc(10)
>11
>
>The definition of curry is:
>
>class curry:
>   def __init__(self, func, *fixed_args):
>      self.func = func
>      self.fixed_args = fixed_args
       self.__name__ = '<curried %s>' % func.__name__
>
>   def __call__(self, *variable_args):
>      return apply(self.func, self.fixed_args + variable_args)
>
>This idiom is where my python enlightenment began.
>
That is a cool class. I added the line to give the result a name, so my timing harness
could show it in the comparison of:

    curryinc = curry(int.__add__, 1)
and
    fasterinc = int.__add__.__get__(1)

Which shows that (as I'm sure is no surprise to you, but might be worth mentioning) you pay
a pretty steep price in performance vs something close to optimum.

BTW, I'm not sure the fasterinc way of doing what I did is going to be an ongoing feature,
so I'm not recommending it. But it seems like nice enough a way to generate an efficient
function-arg pair that acts like a bound method but which isn't, so maybe either it should
be blessed or some sanctioned version should be available? I just used it because it's
something fast to compare with. The name shows up as __add__.

[ 7:21] C:\pywk\clp\curry>timefuns timecurry -c fasterinc -i 1 -c curryinc -i 1 -n 100000
           timing oh:  0.000013  ratio
             __add__:  0.000004   1.00
   <curried __add__>:  0.000047  12.13

[ 7:22] C:\pywk\clp\curry>timefuns timecurry -c fasterinc -i 1 -c curryinc -i 1 -n 100000
           timing oh:  0.000013  ratio
             __add__:  0.000004   1.00
   <curried __add__>:  0.000047  13.12

12 or 13 times slower is pretty heavy. Of course it's very general. But a function factory
version seems to be about twice as fast (warning not tested beyond here ;-), e.g.:

def curryfun(func, *fixed_args):
    curriedname = 'curried_f_%s' % func.__name__
    exec """\
def %s(*variable_args):
    return func(*(fixed_args+variable_args))
""" % curriedname in vars()
    return vars()[curriedname]

[ 7:30] C:\pywk\clp\curry>timefuns    timecurry -c fasterinc -i 1 -c curryinc -i 1 -c curryinc_f
 -i 1 -n 100000
           timing oh:  0.000012  ratio
             __add__:  0.000004   1.00
   <curried __add__>:  0.000047  13.06
   curried_f___add__:  0.000025   6.83

Don't know if Dr. Who is relevantly cooking in India ;-)

Regards,
Bengt Richter



More information about the Python-list mailing list