@ggorlen offers an efficient solution using reduce. Here's a recursive form -
# right-to-left composition
def compose(f = lambda x: x, *funcs):
if not funcs:
return f
else:
return lambda x: f(compose(*funcs)(x))
# left-to-right composition
def compose(f = lambda x: x, *funcs):
if not funcs:
return f
else:
return lambda x: compose(*funcs)(f(x))
Using separate definitions for identity and comp2 may make it easier to see how things are working -
def identity(x):
return x
def comp2(f, g):
return lambda x: f(g(x))
# right-to-left composition
def compose(f = identity, *funcs):
if not funcs:
return f
else:
return comp2(f, compose(*funcs))
# left-to-right composition
def compose(f = identity, *funcs):
if not funcs:
return f
else:
return comp2(compose(*funcs), f)
Here's a complete demo using left-to-right compose and a curry helper. Because curry accepts function as input, we can conveniently use it as a decorator too -
def curry(arity):
def loop(f, n, args):
return f(*args) if n == 0 else lambda x: loop(f, n - 1, (*args, x))
return lambda f: loop(f, arity, ())
@curry(2)
def add(x, y):
return x + y
@curry(2)
def mul(x, y):
return x * y;
myfunc = compose(add(1), mul(2), mul(2), mul(2))
print(myfunc(0))
# (((0 + 1) * 2) * 2) * 2
# 8