2

I need to write a python function called 'concat' that takes any two functions as input, and returns a function, which is the concatenated function of the two input functions (i.e. it takes f1 and f2, and returns f1◦f2).

I tried this:

def concat(f1,f2):
    return f1(f2)

So for example, if f1 and f2 are:

def f1(x):
    return x+2
def f2(x):
    return x*2

then, concat(f1,f2) should return: (x*2)+2

I want to be able to use it like this:

a = concat(f1,f2)
a(5)

But I get an error:

TypeError: unsupported operand type(s) for +: 'function' and 'int'

I know I can define the function like this:

def concat(f1,f2,x): 
    return f1(f2(x))

But that is not what I want; I want to be able to create instances of the concat function, which then can be called with any x.

Qunatized
  • 197
  • 1
  • 9

3 Answers3

6

You need to return a new "wrapper" function. One option is to use a lambda expression:

def concat(f1, f2):
    return lambda x: f1(f2(x))

DOCS: https://docs.python.org/3/tutorial/controlflow.html#lambda-expressions

VisioN
  • 143,310
  • 32
  • 282
  • 281
  • 1
    I'd like to add to this that creating a lambda function in-place may be the better alternative to calling a function that does that. Everybody knows `lambda` but the meaning and operation of `concat()` aren't universally known. – Ulrich Eckhardt Jan 01 '21 at 15:48
  • Isn't function *composition* the right word? – Ivan Jan 01 '21 at 15:53
  • 1
    Yes, the OP is aiming to make a functional composition. I didn't go to term definitions in the answer, as it doesn't really affect the solution for his quite clearly described problem. Nevertheless, he will probably find your comments useful. – VisioN Jan 01 '21 at 15:59
2

I think what you want is a closure.

def concat(f1, f2):
  def f3(x):
    return f1(f2(x))
  return f3

Functions in Python are considered first class objects. This allows them to be created and manipulated like normal variables. In this case concat is constructing a new function that composes two functions.

The second property being used here is lexical scoping. The new function retains access to the local variables where it was defined not where it is executed. This allows the returned function to be run anywhere without losing access to the composite functions.

gph
  • 1,045
  • 8
  • 25
2

Here's a possible solution to compose more than two functions. Using a closure to loop through the functions and use the previous function's result as argument for the next one:

def compose(*fns):
    def F(x):
        for fn in fns[::-1]:
            x = fn(x)
        return x
    return F

Then define your composition and call:

>>> F = compose(f1, f2, lambda x: x+1)
>>> F(4)
12 # f1(f2(f3(x)))
Ivan
  • 34,531
  • 8
  • 55
  • 100