I realized I started using lambda
without explaining what that is. My
apologies.
In math class you probably defined functions by writing something like the following:
f(x) = x * 2
This defines a function named f
which takes a value x
and returns twice that
value, 2 * x
.
In Python we would write this as:
def f(x):
return x * 2
In math and in programming, we can have functions that take functions as arguments. For example:
g(h, x) = h(h(x))
The function g
has two parameters: another function h
and a value x
. It
applies h
to x
twice. Let's look at what happens when we apply g
to f
,
from above, and 10
:
g(f, 10) = f(f(10))
= f(10 * 2)
= f(20)
= 20 * 2
= 40
We can write the same thing in Python:
def g(h, x):
return h(h(x))
g(f, 10)
OK, so what's this got to do with lambda? Well, mathematicians and programmers
often want to define a new function without giving it a name, like f
. These
functions are usually used only once as an argument to another function. In math
a function without uses the greek symbol lambda and in Python we use the word
lambda
.
In math we write λ VARIABLE. BODY
:
λ x. x * 2
In Python we write lambda VARIABLE: BODY
:
lambda x: x * 2
Instead of writing this:
g(f, 10)
We could write this:
g(λ x. x * 2, 10)
In Python that looks like:
g(lambda x: x * 2, 10)
So, in our inner_product
example we could have written this:
def compare_two_genotypes(Xim, Xjm):
return hl.int((Xim == 1) & (Xjm == 1))
def add(l, r):
return l + r
shared_hets = X.T.inner_product(
X,
compare_two_genotypes,
add,
hl.float(0),
hl.agg.sum
)
And maybe we should have! Using long names helps us understand the code. Here's the lambda version again:
shared_hets = X.T.inner_product(
X,
lambda Xim, Xjm: hl.int((Xim == 1) & (Xjm == 1)),
lambda l, r: l + r,
hl.float(0),
hl.agg.sum
)