In a past post we introduced functions and their advantages. In this post, we will extend on the functionality of functions by introducing higher-order functions. Particularly, we’ll see how to include functions among the parameters of a function with an example.
A higher-order function is a function that either:
- Accepts a function among its parameters.
- Returns a function.
In other words, a higher-order function is a function that deals with other functions in some way.
Why are they important?
We can see higher-order functions as an extra “tool” in our programming tool-box. This extra tool allows us greater expressive possibilities while using functions.
Let’s see an example.
Accepting a function as a parameter
Let’s say we have a function with two parameters:
f(a, b). Usually, the parameters
b are variables (numbers, strings, lists, etc). But if any of
b is a function, then
f is a higher-order procedure.
As a concrete example, let’s say we want to filter even values in a list. We could do it with the following function:
def filter(numbers): result =  for number in numbers: if number % 2 == 0: result.append(number) return result
numbers is a list of numbers. For example, if we call
filter([1, 2, 3, 4]), it will return the list of even numbers
Now, let’s say we want to filter odd numbers instead. We can do it with minor changes on the test condition to append elements to the
... if number % 2 == 1: ...
See that the general structure of
filter has not changed. In more general terms, the
if clause establishes the condition that has to be satisfied in order to include a number in the filtered list. This is the motivation we needed to create higher-order functions. Let’s see how to generalize
Defining higher-order functions
Instead of changing the test performed by the
if clause at each time, we can generalize it by using a
... if test(number): ...
test is a function that returns
True when its argument satisfies a certain condition and
False otherwise. To include this function inside
filter‘s body, we just need to include it among its parameters as we did for
def filter(numbers, test): result =  for number in numbers: if test(number): result.append(number) return result
See that the
test parameter is a function called at the
if clause. Congratulations! You have just defined a higher-order function. It wasn’t difficult, right? We just included the function parameter among the other parameters and then used it in the function’s body.
A general recipe for higher order functions of this kind would be:
def function(param_1, ..., func, ...): # Function's body # It's possible to call func(...) here.
Among the paramaters
param_1, … we have a function parameter:
func. By including it among the other parameters, we have now the option of using it inside function’s body with all the advantages that that entails.
Let’s see how to apply it to filter even values. We can test if a number is even with:
def even(n): return n % 2 == 0
If we call
even(10), it will return
True. If we call
even(7), it will return
False, and so on.
Finally, to filter even numbers we call:
>>> filter([1, 2, 3, 4, 5], even) [2, 4]
filter with the arguments
numbers = [1, 2, 3, 4, 5] and
test = even.
What’s the big deal?
While this example may seem trivial, we have in fact defined a very powerful function. Here are some examples:
# Helper functions def odd(n): return n % 2 == 1 def greater_5(n): return n > 5 def is_a(char): return char == 'a'
>>> filter(numbers, odd) [1, 3, 5, 7, 9] >>> filter(numbers, greater_5) [6, 7, 8, 9] >>> filter(['a', 'b', 'c'], is_a) ['a']
filter, we’re able to perform any test on a list of numbers. Here we have seen tests using the
greater_5 functions to filter odd values and numbers greater than 5, respectively. Similarly, by just defining a suitable function, we could filter prime numbers, perfect squares, etc.
In fact, we can apply the filter function to any kind of list! See that the last example was passed a list of characters:
['a', 'b', 'c'] . We filtered it with
is_a to get only the
'a' characters present in the list.
We have seen how to include functions among the parameters of another function, creating higher-order functions. This additional feature allows us a wider range of expressive capabilities using functions. In a future post, we will see how to return functions using functions, a different kind of higher-order function.