James Tauber

journeyman of some

blog > 2008 > 11 > 01 >

Two Fun(ctional) Questions

Consider the following series of functions:

def x(a): if callable(a): return lambda i: a(i) else: return a

then

def x(a): def xx(b): if callable(b): return lambda i: a(b(i)) else: return a(b) return xx

then

def x(a): def xx(b): if callable(b): def xxx(c): if callable(c): return lambda i: a(b(c(i))) else: return a(b(c)) return xxx else: return a(b) return xx

and so on...

Two questions:

Categories:
prev « python » next

Comments (23)

___ on Nov. 1, 2008:

1) pass all possible callables in one call to x - iterate over args

2) function composition (http://en.wikipedia.org/wiki/Function_composition)

is that any help?

ZeD on Nov. 1, 2008:

Hi! I don't know exactly what you want :)
but I think you want a "something" as you can call like

result = x(f1)(f2)(f3)...(fn)(number)

exact?

To my mind it behave like a sort of "foldr (o reduce) + function composition"

I have in mind 2 possible solutions, either using a "compose" function:

compose = lambda F, G: lambda *args, **kwd: F(G(*args, **kwd))

first solution is more easy to write:

def xx(*args):
ret = lambda i: i # identity function
for arg in args:
if callable(arg):
ret = compose(ret, arg)
else:
return ret(arg)
return ret

but the downsite is the invocation:

>>> l = lambda x: x+1
>>> print 0, xx(0), xx(l)(0), xx(l, l)(0), xx(l, l, l)(0)
0 0 1 2 3
>>>

another solution involves classes and __call__:

class Xx2(object):
def __init__(self, passing_object=None, error=True):
if passing_object and error:
raise "You should not pass arguments to __init__!"
self.passing_object = passing_object

def __call__(self, param):
if callable(param):
return Xx2(compose(self, param), error=False)
else:
if self.passing_object:
return self.passing_object(param)
else:
return param

>>> xx2 = Xx2() # I want to separate __init__ from __call__
>>> print 0, xx2(0), xx2(l)(0), xx2(l)(l)(0), xx2(l)(l)(l)(0)
0 0 1 2 3
>>>

Maybe you can find something useful in my ideas

ZeD on Nov. 1, 2008:

uff, sorry for double posting, but I lost all indentation...
look at http://pastebin.com/m604b264d

James Tauber on Nov. 1, 2008:

ZeD, yes what I'm looking for is the best way to support things like

result = (f1)(f2)(f3)..(fn)(arg)

where f1, etc are some wrapped version of a normal unary function. My 'x' function works for fixed depth, but I was wondering what the best solution is for arbitrary depth.

e.g. if you define:

neg = x(lambda i: -i)
double = x(lambda i: 2*i)
square = x(lambda i: i*i)

then

(neg)(double)(square)(2) == -8

So the question is (1) how to implement 'x' here; and (2) what x is called. We're doing function composition but that's not what x is doing. It's not currying, it's not thunking, it's not trampolining. Is there a word for it?

rgz on Nov. 1, 2008:

nested_caller = lambda(fn_list, *args, **kwargs): reduce(lambda v, fn: fn(v), reversed(fn_list[:-1]), fn_list[-1](*args, **kwargs))

Writing the whole as a lambda for your morbid pleasure.

Now for all I care a == (labmda i: a(i)) so the first function is irrelevant. From then on I don't know if you have your callables avalable for the single call or not.

What is exactly what you are trying to achieve?

James Tauber on Nov. 1, 2008:

ZeD, your class-based solution pretty much does what I was looking for, although only needs to be the first thing called, not a wrapper for each function.

In other words, neg, double, square don't need to be individual wrapped.

This is just the first step in my plans so we'll see if it's adaptable to my other needs :-)

rgz on Nov. 1, 2008:

Now I see you want something like this?

http://pastebin.com/f6d94d676

James Tauber on Nov. 1, 2008:

ZeD, I wasn't sure about the need for the error checking in your class-based solution so I removed it, inlined the compose function and also switched checking for None to just defaulting passing_object to the identity function. Oh, and I also renamed passing_object to just 'func'.

The result:

class F(object):
....def __init__(self, func=lambda i:i):
........self.func = func
....
....def __call__(self, param):
........if callable(param):
............return F(lambda i: self(param(i)))
........else:
............return self.func(param)

rgz on Nov. 1, 2008:

Can also be called as
TotalRecall(f1)(f2)(f3)(f4)(f5)(arg)
So you don't have to stuff inside f1

This is called Currying it seems http://en.wikipedia.org/wiki/Currying

Sorry for reposting it's 3 in the morning here, can't you join all my posts together?

Stas Shtin on Nov. 1, 2008:

Hi James,

Here's a likely solution to your problem: http://pastebin.com/f524a7ae4 with a couple of tests.

Also, I think that last two functions in your post are not quite correct - I think it looks like they won't accept one non-callable variable, so I've changed them a bit.

Stas Shtin on Nov. 1, 2008:

Hi James,

Here's a likely solution to your problem: http://pastebin.com/f524a7ae4 with a couple of tests.

Also, I think that last two functions in your post are not quite correct - I think it looks like they won't accept one non-callable variable, so I've changed them a bit.

ZeD on Nov. 1, 2008:

@James

I separate the behaviour of __init__ and __call__ because I wasn't sure of what you wanted :)

The main problem in your solution is: what appened if you apply F to a "normal" arg (not a callable one)?

In [2]: F(0)
Out[2]: <__main__.F object at 0x824962c>

In [3]: F()(2) # implicitly identity
Out[3]: 2

If this is not a problem for you, continue :)

(I assume it was a problem looking for the first x() you wrote... it can return a value (not a identity function) if the parameter was a non callable)

Robert Brewer on Nov. 1, 2008:

result = i; for f in funcs: result = f(result)

Why anyone would want to trade that for a style where you can't iterate over the functions without calling them is beyond me.

Zachary Voase on Nov. 1, 2008:

I've created a function in about 10 lines that seems to solve this problem perfectly. It works from the inside-out; the last function given will be the first to be applied. I suppose it could be made to work the other way around, but then you could always just reverse the order of arguments.

You can find it here: http://pastebin.com/f13626dd8

Laurence Rowe on Nov. 1, 2008:

Why not use reduce?

In [7]: def f1(x):
print "f1(%s)" % x
return 2 * x
...:

In [10]: def f2(x):
print "f2(%s)" % x
return x + 1
....:

In [13]: reduce(lambda x,f: f(x), [f1, f2], 2)
f1(2)
f2(4)
Out[13]: 5

James Tauber on Nov. 1, 2008:

I was deliberately vague about my ultimate goal in the original post, although I've said more in comments.

I think what I'm basically looking for is to being able to treat composition and application the same.

I want neg(5) == -5 and double(5) == 10 but neg(double) to give me a function that will do both, i.e. neg(double)(5) == -10

Furthermore, I want to be able to define:

K = lambda x: lambda y: x
S = lambda x: lambda y: lambda z: x(z)(y(z))

and take things from there...

Note K(neg)(double)(5) works without modification, but I'd like (neg)(double)(5) to as well.

S(K)(K)(5) also works without modification as does S(K)(K)(neg)(5) but not (neg)(S)(K)(K)(neg)(5)

Mike on Nov. 1, 2008:

Here's my 2 cents:

class Composable(object):
...from functools import partial
...def __init__(self,func):
......self.func = func
...def __call__(self,*args):
......if isinstance(args[-1],Composable):
.........return partial(args[-1],self.func,*args[:-1])
......else:
.........val = self.func(args[-1])
.........for func in args[:-1]:
............val = func(val)
.........return val

>>> import math

# can wrap builtins
>>> sin = Composable(math.sin)
>>> exp = Composable(math.exp)
>>> sqrt = Composable(math.sqrt)

# wrapped funcs work normally
>>> sin(math.pi/4.0)
0.70710678118654746

# can compose wrapped funcs
>>> (sin)(exp)(sqrt)(25.0)
-0.68769141169451153

# can define a func as the composition
# of wrapped funcs
>>> newfunc = (sin)(exp)(sqrt)
>>> newfunc(25.0)
-0.68769141169451153

Doug Napoleone on Nov. 1, 2008:

Sounds like 'Stack Composition' to me (though not exactly that).

A real test case for the example James has given would look more like this:

scomp(math.sin)(math.exp)(math.sqrt)(25)

Only the first function needs to be composable. James gave a loaded hint with the word 'recursion' which I am shocked no one has picked up on yet.

Mike on Nov. 1, 2008:

The class I gave above had the limitation that composed functions could not always be used in another composition. For example:

x = (sin)(exp)
y = (x)(cos)
z = (cos)(x) <= wouldn't work

Here is another stab at it:

class Composable(object):

...def __init__(self,func):
......if isinstance(func,(list,tuple)):
.........self.flist = func
......else:
.........self.flist = [func]

...def __call__(self,arg):
......if isinstance(arg,Composable):
.........return Composable(arg.flist + self.flist)
......else:
.........return reduce(lambda a,f:f(a),self.flist,arg)

# some tests
>>> sin = Composable(math.sin)
>>> exp = Composable(math.exp)
>>> sqrt = Composable(math.sqrt)
>>> sqr = Composable(lambda x:x*x)
>>> pi = math.pi

>>> sin(pi/4.0)
0.70710678118654746

>>> sin(pi/4.0)**2
0.49999999999999989
>>> (sqr)(sin)(pi/4.0)
0.49999999999999989

>>> (exp)(sqr)(sin)(pi/4.0)
1.648721270700128

>>> x = (sqrt)(exp)
>>> y = (sqr)(sin)
>>> (x)(y)(pi/4.0)
1.2840254166877414
>>> (y)(x)(pi/4.0)
0.9919533865352167

>>> z = (x)(y)
>>> z(pi/4.0)
1.2840254166877414

UltraBlue on Nov. 3, 2008:

I had some trouble with this and tried alot of stuff (you can see on the link to the pastebin). Partial functions actually make this look better than lambda.

eventually this one stuck:
def id(x): return x

from functools import partial

def funcomp(gunc, func):
'Composites two functions'
return lambda x: gunc(func(x))

def scompb(a, func = id):
'Super Function Compositer'
if callable(a):
new_comp = funcomp(func, a)
new_scomp = partial(scompb, func = new_comp)
return new_scomp
else:
result = func(a)
return result

UltraBlue on Nov. 3, 2008:

Wow, no tabs? How about spaces...

from math import *

def id(x): return x

def funcomp(gunc, func):
'Composites two functions'
return lambda x: gunc(func(x))

def scompb(a, func = id):
'Super Function Compositer'
if callable(a):
new_comp = funcomp(func, a)
new_scomp = partial(scompb, func = new_comp)
return new_scomp
else:
result = func(a)
return result
print(sin(cos(tan(x))))
print(scompb(sin)(cos)(tan)(0.3))

Chung-chieh Shan on Nov. 3, 2008:

Check out <a href="http://www.eecs.usma.edu/webs/people/okasaki/hw02.ps">Techniques for Embedding Postfix Languages in Haskell</a> (PostScript link) by Chris Okasaki.

mathrick on Nov. 4, 2008:

Now, that one was a bit of a bitch, I had to abandon it yesterday and could only finish it today. But here's my solution, from what I can see, it's the shortest (although very similar to Zachary's, as it turns out), and doesn't use any mutable state.

http://pastebin.com/f6d34139b

Created: Nov. 1, 2008
Last Modified: Nov. 1, 2008
Author: James Tauber