These 2 lines of code are from an exam paper and I'm trying to figure out how to properly visualise how the variables move about. The output is 8.
f = lambda x, y: lambda z: (x)(y)(z)
print((f)(lambda x: lambda y: x, lambda z: z * 2)(3)(4))
I've tried using online python visualiser websites but I still can't understand how to process works. Is it possible if someone is kind enough to rewrite this as a 'def function()'?
CodePudding user response:
f
can be written as a function as follows:
def f(x, y):
def inner(z):
return x(y)(z)
return inner
f
takes two functions, x
and y
. x
is a function that accepts another function (y
), and returns a third function that accepts an argument z
.
The print statement calls f
with a couple of anonymous functions called in-line:
print((f)(lambda x: lambda y: x, lambda z: z * 2)(3)(4))
We can rewrite this print statement as follows:
print((f)(lambda_x, lambda_z)(3)(4))
with lambda_x defined as:
def lambda_x(x):
def lambda_y(y):
return x
return lambda_y
lambda_x
is a function that accepts some function x
. It then creates a function lambda_y
, that accepts some argument y
. regardless of what y
is, lambda_y
returns the original function passed to lambda_x
- x
. In other words, lambda_x
can be rewritten as:
def lambda_x(x):
return x
So you can see that y
is just a red herring here.
lambda_z
can be rewritten as:
def lambda_z(z):
return z*2
When we run the print statement, we call f
with the arguments lambda_x
and lambda_z
. In running f
, we create a new function inner
, that calls lambda_x
with the argument lambda_z
. We've seen already that if we call lambda_x
and pass to it some function, we simply get that function back. So when we call f
with lambda_x
and lambda_z
, what we get out of it is just lambda_z
.
The definition of lambda_x
, however, requires a superfluous argument y
to be passed - to which nothing is done, and from which no value is obtained. In this print statement, 3
plays this role. You can rerun your original two lines of code with anything in place of 3
, and get the same result - try it with 'foo'
, or 3 j
or any other argument of any other type in place of 3
in the print statement - it'll make no difference.
f
therefore returns the function lambda_z
, which consumes the final argument 4
, and per the definition of lambda_z
, returns 8
.
Blow by blow:
If you throw in print statements along the way, as follows, you can follow along the various function calls in the resulting output. Here, I've set up the print
statement with 'foo'
in place of 3
to demonstrate its superfluity:
def f(x, y):
print (f'Calling "f", with arguments {x} and {y}')
def inner(z):
print (f'Calling function "inner" with argument {z}')
return x(y)(z)
print (f'returning "inner"')
return inner
def lambda_x(x):
print (f'calling lambda_x with argument {x}')
def lambda_y(y):
print (f'calling lambda_y with argument {y}, returning {x}')
return x
return lambda_y
def lambda_z(z):
print (f'calling lambda_z with argument {z}')
return z*2
print((f)(lambda_x, lambda_z)('foo')(4))
With the result:
Calling "f", with arguments <function lambda_x at 0x0000017EC49109D0> and <function lambda_z at 0x0000017EC4910940>
returning "inner"
Calling function "inner" with argument foo
calling lambda_x with argument <function lambda_z at 0x0000017EC4910940>
calling lambda_y with argument foo, returning <function lambda_z at 0x0000017EC4910940>
calling lambda_z with argument 4
8
Hopefully that helps clarify?