简体   繁体   中英

python lambda evaluate expression

I am trying out lambda in python and came across this question:

def foo(y):
    return lambda x: x(x(y))
def bar(x):
    return lambda y: x(y)
print((bar)(bar)(foo)(2)(lambda x:x+1))

can someone explain/breakdown how this code works? I am having problems trying to figure out what is x and y.

Lambda functions are just functions. They're almost syntatic sugar, as you can think of this structure:

anony_mouse = lambda x: x # don't actually assign lambdas

as equivalent to this structure:

def anony_mouse(x):
    return x

(Almost, as there is no other way of getting a function without assigning it to some variable, and the syntax prevents you doing some things with them, such as using multiple lines.)

Thus let's write out the top example using standard function notation:

def foo(y):
    # note that y exists here
    def baz(x):
        return x(x(y))

    return baz

So we have a factory function, which generates a function which... expects to be called with a function (x), and returns x(x(arg_to_factory_function)). Consider:

>>> def add_six(x):
        return x + 6
>>> bazzer = foo(3)
>>> bazzer(add_six) # add_six(add_six(3)) = 6+(6+3)

I could go on, but does that make it clearer?

Incidentally that code is horrible , and almost makes me agree with Guido that lambdas are bad.

The 1st '(bar)' is equal to just 'bar' so it is an ordinary function call, the 2nd — argument to that call, ie bar(bar) — substitute 'x' to 'bar' there any you will get what is result of bar(bar); the'(foo)' argument passing to the result of bar(bar) it will be a lambda-function with some arg. — substitute it to 'foo' and get result and so on until you reach the end of expression

I slightly modify your original function to make clearer what's going on (so it should be clearer which parameter is callable!)

# given a function it evaluates it at value p
def eval(func):      # your foo
    return lambda p: func(p)

# given a value p perform a double composition of the function at this value (2-step recursion)
def iter_2(p):      # your bar
   return lambda func: func(func(p))

increment = lambda x: x + 1  # variable binding only for readability

This example is quite hard to understand because one of the function, eval just do nothing special, and it composition is equivalent to the identity. ... so it could be quite confusing.

  • (foo)(2)(lambda x:x+1)) :
x = 2
iter_2(x)(increment) # increment by 2 because iter_2 calls increment  twice
# 4
  • idempotency: (or composition with itself return the identity function)
increment(3) == eval(increment)(3)
# True
# idempotency - second composition is equivalent to the identity
eval(increment)(3) == eval(eval)(increment)(3)
# True
eval(increment)(3) == eval(eval)(eval)(increment)(3)
# True
# ... and so on
  • final: consequence of idempotency -> bar do nothing, just confusion
eval(eval)(iter_2)(x)(increment) == iter_2(x)(increment)
# True

Remark : in (bar)(bar)(foo)(2)(lambda x:x+1) you can omit the brackets around the 1st term, just bar(bar)(foo)(2)(lambda x:x+1)

Digression : [since you example is quite scaring]

Lambda functions are also known as anonymous function. Why this? Simply because that they don't need to be declared. They are designed to be single purpose, so you should "never" assign to a variable. The arise for example in the context of functional programming where the basic ingredients are... functions. They are used to modify the behavior of other functions (for example by decoration.). Your example it is just a standalone syntactical one... essentially a nonsense example which hides the truth "power" of the lambda functions. There is also a branch mathematics which based on them called lambda calculus .

Here a totally different example of application of the lambda functions, useful for decoration (but this is another story):

def action(func1):
   return lambda func2: lambda p: func2(p, func1())

def save(path, content):
   print(f'content saved to "{path}"')

def content():
   return 'content' # i.e. from a file, url, ...

# call
action(content)(save)('./path')

# with each key-parameter would be
action(func1=content)(func2=save)(p='./path')

Output

content saved to "./path"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM