Lambda
Learning Objectives
At the end of this sub-unit, students should
- understand anonymous function.
- know how to write anonymous function.
Generalized Operation
Previously, we implemented two functions map_n
and filter_n
.
These two operations are actually quite common.
How can we generalize this?
Using the technique that we have learnt so far, we can simply say that there is a function F(item)
and a predicate P(item)
1.
We then implement our map_n
and filter_n
more generally as mapf
and filterf
.
Map
We can then define map_n
by first defining F
as follows.
Filter
We can then define filter_n
by first defining P
as follows.
Immediately we the problem with this generalization because there is no place where we can define n
correctly.
After all, n
should only be defined inside the function map_n
and filter_n
.
This is problematic.
How does Python implement the generalized version of map
and filter
?
Anonymous Function
The answer is the concept of an anonymous function. This is known as lambda. Lambda is an expression in Python so it can be used in any place where an expression can be used. In Python, the syntax is shown below.
Syntax
Here, ❬ params ❭
may be empty.
Note that we can only accept ❬ expression ❭
and not a general statement.
We will not really be discussing the semantics.
Instead, we will provide a translation to a named function.
But because there is no name for lambda expression, we will use underscore (i.e., _
) as the name.
The equivalent named function is as follows.
We write them side by side.
What this shows is that if we are confused what the behavior of lambda
is, we can imagine that there is a function that perform the computation.
The confusing part will be the scoping, which we will touch on later.
For now, let us construct a few lambdas to illustrate what anonymous functions we can create.
Try to generalize this into several parameters.
Anonymous Functions are Functions
Although they are anonymous, they are actually still functions. The following concepts are part of the topic of higher-order functions, but we will only use a simplified version of it. What we want to do now is to assign name to these anonymous function. We do that by assigning the lambda into a variable.
Now that it has a name add
, we can use it as if add
is the following function by our conversion method.
This is shown in the sample run above.
The equivalent named function is shown below.
Bad Practice
Since lambda is an expression, it can be used in all places where expression can be accepted.
In add(1, 2)
, add
is actually an expression.
Which means, we can replace add
with its lambda definition.
This gives us the following near incomprehensible code.
What Lambda Should Do
We already know the capability of lambda. In fact, all lambda can be translated into the equivalent named function. What is the limitation of lambda over named function?
If we look back at the conversion, the first thing we should note is the lack of statement in the named function. The named function only has a single return statement. So the following named function cannot be represented as lambda easily. Still, by some smart conversion below, we can still write it as lambda.
What about things that lambda really cannot do? Consider a logging mechanism that utilized a mutable list as a log. Here we need complicated tricks to actually represent this as lambda. The trick is to construct an intermediate tuple where the result is the second element of the tuple.
As before, instead of forcing our way to solve with lambda, we should embrace lambda as well as its apparent limitation. What you should do with lambda is to create what we call as a pure function. A pure function should only depend on the input parameter and does not modify anything.
Selective Impurities
But pure function is very restrictive.
The example using log
shows that we can actually use the variables from the scope.
So we relaxed our restriction a little bit.
There is no name for this kind of function but we can say that this is a straightforward function.
Note that this is our own naming convention.
Straightforward Function
A straightforward function is a function that does not modify anything and uses only the parameters or the variable from the scope (or both).
Using straightforward function, we can actually have a way to define F(item)
and P(item)
that can use n
correctly.
We do this by defining F(item)
and P(item)
inside the function map_n
and filter_n
.
Then F(item)
and P(item)
are both straightforward function because n
is now part of the scope.
There is still one more component that is missing to fully use this in mapf
and filterf
.
What is missing is the way for mapf
and filterf
to use F(item)
and P(item)
that is defined inside map_n
and filter_n
.
We will use the following trick that you do not have to understand.
The main thing to note is that it can be done.
Python has an implementation of map
and filter
so that we do not have to use such tricks.
We only need to know that there is a way.
Map
We can then define map_n
by first defining F
as follows.
Filter
We can then define filter_n
by first defining P
as follows.
-
Recap, a predicate is simply a function that returns a boolean value. ↩