What will this Python 2 code print?
1 2 3 4 |
|
This section requires Javascript.
You are seeing this because something didn't load right. We suggest you, (a) try
refreshing the page, (b) enabling javascript if it is disabled on your browser and,
finally, (c)
loading the
non-javascript version of this page
. We're sorry about the hassle.
Of course, we can simply run it:
but that kind of defeats the point. So instead, we will analyze what this code does. The following solution is aimed for people that have never known
lambda
s and functional programming before, but can understand functions in Python.lambda
is just a fancy way to say "I want a 'simple' function, and am too lazy to give it a name", where "simple" means "computes a single statement and immediately returns its value". In other words, if we haveprint (lambda x: x*x)(2)
, it's more or less the same as:And since
lambda
is mostly used just to avoid giving names to simple computations, assigning a lambda to a name more or less defeats the point; the three assignments are equivalent to:(Note the renaming of the arguments; we don't have to, but it makes us to understand the code much easier.)
Now, we can see that
x
is a function that takes another functionx0
(with one argument), and appliesx0
to with itself being the argument.z
is just a function that increments its input. What isy
?We first need to know what a function inside a function means. Normally, we write a function with two variables as:
We invoke it with
our_function(1, 2)
, for example. We need to give both arguments at the same time; doingour_function(1)
won't work. What if we want to put just some of the arguments, not all, and complete it at a later time?Currying is a way to do this. Instead of having a function that takes two arguments (and thus must be supplied at the same time), we just take one argument instead, which returns another function that takes the second argument. That is, we do:
This way, we can call
our_function(1)
; now we have a function that can be supplied with the second argument later. As the expense, this is the only way we can call the function; if we want to call it with two arguments like before, we need to go in a roundabout way(our_function(1))(2)
; we first give the first argument1
, and immediately give the second argument2
to the result.As an example, we might have this:
And now,
addition_no_currying(1, 2)
will return3
, but we cannot doaddition_no_currying(1)
. Instead, we can putaddition_with_currying(1)
, which will give a function that increments its input (because giving it the argumentb
will return the result1+b
). And if we want to just compute a single addition, not making a function to increment, we can do that with(addition_with_currying(1))(2)
which will also return3
.Now, we can understand
y
. It is a curried function that takes two argumentsy0
andz0
, and appliesy0
onz0
twice (it computey0(z0)
, takes its result, and uses it as the argument fory0
).To understand these, it might be better to illustrate them with examples:
In the first example, we apply
x
, thus we get(lambda a: str(a))(lambda a: str(a))
. The firstlambda a: str(a)
indicates that we want the argument to be converted to a string. The secondlambda a: str(a)
is just a function object that we never get to use anywhere; it's simply treated as an object, and it's given to the firstlambda a: str(a)
, which prints it as a string. When Python converts a function to a string, it just gives<function [name] at [address]>
, wherename
is the name of the function, if any, andaddress
is the address of the function object in memory; in this case, it's a lambda that we never give a name into, so it appears as<lambda>
, and its address is0x02B85780
when I run it. (Running it on different computers/times can produce different addresses.)In the second example, we apply the increment function
lambda a: a+1
twice to1
. The first time, we get2
; the second time, we apply it on2
to get3
.Now that we know what they do, let's rename them so we can understand the logic better:
x
isapply_to_itself
,y
isapply_twice
, andz
isincrement
. So the modified code is:Now with the knowledge of lambda in hand, we can try to parse what the statement
(apply_to_itself)(apply_twice)(increment)(0)
means.First,
apply_to_itself
doesn't need any parentheses; you can try that(str)(123)
andstr(123)
give the same result'123'
. So now we haveapply_to_itself(apply_twice)(increment)(0)
.Function application is left-associative; that is,
f1(f2)(f3)
means(f1(f2))(f3)
. So we can add parentheses to the above to get((apply_to_itself(apply_twice))(increment))(0)
.The first application is easy;
apply_to_itself(apply_twice)
can be evaluated toapply_twice(apply_twice)
. Our statement now reads((apply_twice(apply_twice))(increment))(0)
Now,
apply_twice
needs to take two consecutive arguments. We have the first one,apply_twice
. The next one is given afterwards;increment
. Applyingapply_twice
twice onincrement
, we change(apply_twice(apply_twice))(increment)
toapply_twice(apply_twice(increment))
. Note the subtle change of parentheses; that's all the work that is done, but that's because the first argument (the middle text) isapply_twice
. To compare,(apply_twice(str))(increment)
would givestr(str(increment))
. Our statement now reads(apply_twice(apply_twice(increment)))(0)
.Now, consider
apply_twice(increment)
, the part that's inside. We knowapply_twice
needs two arguments. It has one argument,increment
, so we now have a function that takes an argument -- the second argument -- and appliesincrement
twice to it. That is,apply_twice(increment)
is the same aslambda arg2: increment(increment(arg2))
. Our statement now reads(apply_twice( lambda arg2: increment(increment(arg2)) ))(0)
.We're close! The first
apply_twice
needs two arguments. Conveniently, they are supplied right after: the first is the functionlambda arg2: increment(increment(arg2))
, and the second is0
. The result is(lambda arg2: increment(increment(arg2)))((lambda arg2: increment(increment(arg2)))(0))
.The inner portion,
(lambda arg2: increment(increment(arg2)))(0)
, can now be evaluated. Computing the application(lambda x: <stuff>)(arg)
is simply substitutingx
byarg
everywhere in<stuff>
. Thus,(lambda arg2: increment(increment(arg2)))(0)
computes asincrement(increment(0))
, which is2
. Our statement reads(lambda arg2: increment(increment(arg2)))(2)
. We can apply this once more, givingincrement(increment(2))
which returns4
. This is our answer.TL;DR : This problem is a complicated way of saying "add 4".