2.2 Higher-Order Functions
Variables are names bound to values, which can be primitives like 3
or 'Hello World'
, but they can also be functions. And since functions can take arguments of any value, other functions can be passed in as arguments. This is the basis for higher-order functions.
A higher order function is a function that manipulates other functions by taking in functions as arguments, returning a function, or both. We will introduce the basics of higher order functions in this lab and will be exploring many applications of higher order functions in our next lab.
2.2.1 Functions as arguments
In Python, function objects are values that can be passed around. We know that one way to create functions is by using a def
statement:
def square(x):
return x * x
The above statement created a function object with the intrinsic name square
as well as binded it to the name square
in the current environment. Now let's try passing it as an argument.
First, let's write a function that takes in another function as an argument:
def scale(f, x, k):
""" Returns the result of f(x) scaled by k. """
return k * f(x)
We can now call scale
on square
and some other arguments:
>>> scale(square, 3, 2) # Double square(3)
18
>>> scale(square, 2, 5) # 5 times 2 squared
20
Note that in the body of the call to scale
, the function object with the intrinsic name square
is bound to the parameter f
. Then, we call square
in the body of scale
by calling f(x)
.
As we saw in the above section on lambda
expressions, we can also pass lambda
expressions into call expressions!
>>> scale(lambda x: x + 10, 5, 2)
30
In the frame for this call expression, the name f
is bound to the function created by the lambda
expression lambda x: x + 10
.
2.2.2 Functions that return functions
Because functions are values, they are valid as return values! Here's an example:
def multiply_by(m):
def multiply(n):
return n * m
return multiply
In this particular case, we defined the function multiply
within the body of multiply_by
and then returned it. Let's see it in action:
>>> multiply_by(3)
<function multiply_by.<locals>.multiply at ...>
>>> multiply(4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'multiply' is not defined
A call to multiply_by
returns a function, as expected. However, calling multiply
errors, even though that's the name we gave the inner function. This is because the name multiply
only exists within the frame where we evaluate the body of multiply_by
.
So how do we actually use the inner function? Here are two ways:
>>> times_three = multiply_by(3) # Assign the result of the call expression to a name
>>> times_three(5) # Call the inner function with its new name
15
>>> multiply_by(3)(10) # Chain together two call expressions
30
The point is, because multiply_by
returns a function, you can use its return value just like you would use any other function.