Home > Software design >  Create composite function from arbitrary number of functions
Create composite function from arbitrary number of functions

Time:07-12

I am reading Joseph Howse OpenCV book. In the appendix, he's discussing creation of composite function from 2 other functions, as follows:

def createCompositeFunction(func1, func2):
    return lambda x : func2(func1(x))  

How could I write this for an arbitrary number of functions, like so

def createCompositeFunction(*funcs):
    pass

I assume this should be done using recursion, but I can not wrap my head around it. Any suggestions?

CodePudding user response:

You don't need recursion; this is a simple iterative problem:

def createCompositeFunction(*funcs):
    def apply(x):
        for func in funcs:
            x = func(x)
        return x

    return apply


def f1(x):
    return x   2


def f2(x):
    return x * 3


def f3(x):
    return x / 2


comp = createCompositeFunction(f1, f2, f3)

print("comp(1) =", comp(1))
print("comp(2) =", comp(2))

Running the above code will output:

comp(1) = 4.5
comp(2) = 6.0

CodePudding user response:

You can use accumulate from functools (and keep intermediate results):

from itertools import accumulate

def f1(x): return x   2
def f2(x): return x * 3
def f3(x): return x / 4

def createCompositeFunction(func1, func2):
    return lambda x: func2(func1(x))

# For x=3
l = [f(3) for f in accumulate([f1, f2, f3], createCompositeFunction)]

Output:

>>> l
[5, 15, 3.75]  # <- the last item l[-1] is the final value

CodePudding user response:

@larsks has a pretty nice answer. If you're interested in recursion specifically, here's an option:

def createCompositeFunction(*funcs):
    func = funcs[0]
    funcs = funcs[1:]
    if len(funcs) == 0:
        return func
    return lambda x: func(createCompositeFunction(*funcs)(x))

def square(x):
    return x ** 2


square_thrice = createCompositeFunction(square, square, square)
print(square_thrice(2))

Output:

>>> 256

CodePudding user response:

What you're asking for in functional programming terms is a reducer higher-order function. Python provides functools.reduce to this end:

def reduce(function, iterable, initializer=None):

Where function should be an applicator, iterable is the chain of funcs you want to apply, and initializer is your argument.

Here's a simple example on one argument:

from functools import reduce

def sub1(a):
    return a - 1

def mul2(a):
    return a * 2

def apply(x, f):
    return f(x)

def compose(*fns):
    return lambda x: reduce(apply, fns, x)

print(compose(sub1, mul2)(4)) # => 6

You can partial or lambda in extra args as needed:

from functools import partial, reduce
from operator import mul, sub

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

print(compose(lambda x: sub(x, 2), partial(mul, 3))(4)) # => 6

There are a lot of ways to go with this sort of thing, so I'll leave it at this absent further information about your use case.

As it turns out, this is pretty much a more fleshed-out version of Compute a chain of functions in python.

  • Related