undefined variable in lambda expression - python

I am having trouble understanding the following function.
def make_adder(n):
return lambda x: x + n
plus_2 = make_adder(2)
plus_2(5)
>>> 7
In this function, what does x represent and how does this not result in an error because x is undefined?

The x represents the parameter that the lambda expression receives, this is why it's before the ":".
When you do the plus_2 = make_adder(2) call, the lambda expression substitutes the n with the parameter of the function (2), so now plus_2 equals lambda x: x + 2. When you call plus_2(5) the lambda expression is evaluated, substituting the x with the function parameter (5), so the result is 5 + 2 = 7;

You're defining a function which, given n, returns a function which accepts an argument x and returns x + n. This is called a higher-order function. It doesn't yield an error because you're explicitly returning another function which expects an argument.

Lambda functions are awesome. They allow you to define higher-order functions inline. The general format is lambda args: expression. In this case, x is the argument passed into the lambda function. Because make_adder returns a lambda function, whatever you pass into make_adder is set as n. So when you pass in make_adder(2) you get a lambda function that adds 2 to the argument (x).
Decomposing your original snippet:
def make_adder(n):
return lambda x: x + n
plus_2 = make_adder(2) # returns lambda x: x + 2
plus_2(5) # executes lambda expression, x + 2 with x=5
Starting from scratch:
5 + 2 # 7
plus_two_fn = lambda x: x + 2 # Add 2 when you call plus_two_fn(x)
plus_two_fn(3) # returns 5 (3 + 2)
def plus_num_fn(num):
return lambda x: x + n # Allow you to define plus "any" number
plus_one_fn = plus_num_fn(1) # lambda x: x + 1
plus_one_fn(2) # returns 3 (2 + 1)

In the line below:
plus_2 = make_adder(2)
we're binding the integer object 2 to n.
After that when plus_2 is called using the argument:
plus_2(5)
the integer object 5 would be bind to x when the lambda expression is executed.
This is the runtime execution flow. Since there is no ambiguity or errors in this whole process, the program runs just fine and outputs 7.
Now, to answer your question: the variable x represents whatever value is passed to plus_2() as per your naming.

Related

Reframe pipeline function with a generator

I have a pipeline function that takes an arbitrary number of functions as arguments, it returns a single function helper which contain one argument and this function in turn calls the pipeline function with a single argument iteratively. Here is the code:
def pipeline(*funcs):
def helper(arg):
for func in funcs:
result = func(arg)
arg = result
return result
return helper
And here a test case:
fun = pipeline(lambda x: x * 3, lambda x: x + 1, lambda x: x / 2)
print(fun(3)) #should print 5.0
I was reading on a different question about generators and how they can be used to remember previous state here, and was wondering if I could reframe my current pipeline function to use a generator instead. I need to remember the arg for every func call and currently I'm doing that by storing into a variable, however since generators can remember the last state I was wondering if I could use it instead.
You could (but absolutely shouldn't) create a side-effect based list-comprehension that uses the walrus-operator:
funcs = [lambda x: x * 3, lambda x: x + 1, lambda x: x / 2]
x = 3
[x := f(x) for f in funcs]
print(x)
What makes this even worse than it already is is the fact that if you use a simple generator instead of the list-comprehension the result is different because it doesn't all get executed before the print.
Or you even force it all in one line like this:
print([x := f(x if i>0 else 3) for i, f in enumerate(funcs)][-1])
I think generally a prettier approach would be to just wrap it into a normal for loop:
x = 3
for f in funcs:
x = f(x)
print(x)

python lambda : maximum recursion depth exceeded in comparison

I wrote the following code in Python:
func = lambda x : x * 2
func = lambda x : func(x)
func(6)
When I ran the code above, I got
RecursionError: maximum recursion depth exceeded in comparison
I think the reason maybe : when it runs, it looks like this :
func = lambda x : lambda x: lambda x: (bula bula ...)
But why shouldn't it be like this :
func = lambda x : lambda x : x * 2
Or any other reasons?
When you write func = lambda x : func(x), you are redefining func to be that lambda, which just calls itself repeatedly until it bottoms out at the recursion limit. Consider a def version of the same:
def i_call_myself_forever(x):
return i_call_myself_forever(x)
Your lambda is doing the same thing.
I think what you are trying to do is call the first definition of func from the second. That being the case, just give them distinct names and you'll be fine.
Additionally, you're not correctly understanding what func = lambda x : lambda x : x * 2 means. What's happening is that the top level lambda is returning another lambda, namely lambda x : x * 2. That second level lambda uses the same parameter name x and as such shadows the x from the top level lambda. You could call it as follows
func = lambda x : lambda x : x * 2
intermediate_func = func(5) # 5 is captured but never used
assert intermediate_func(6) == 12 # this is calling lambda x : x * 2
Let's also try a def version to be as clear as possible:
def i_return_a_function(x):
def im_an_inner_function(x):
# my x shadows i_return_a_function's x
return x * 2
return im_an_inner_function # notice how i_return_a_function's x is never used
Finally, I want to emphasize #tdelaney's comment: Sure, there are plenty of examples of people assigning lambdas to a name out there in the wild, but that doesn't mean that doing so is considered good practice. There's some disagreement in the community about how strict to be about this, but my rule of thumb is to only use lambda when the function is a single expression (which is all you can express with a python lambda) that I'm using exactly once.

Python Multiple Function Composition

So i have a homework question, but I'm not sure why I got it wrong / how it works.
once = lambda f: lambda x: f(x)
twice = lambda f: lambda x: f(f(x))
thrice = lambda f: lambda x: f(f(f(x)))
print(thrice(twice)(once)(lambda x: x + 2)(9))
My ans: 25 -> 8*2 +9
Actual ans: 11 -> 2 + 9
What I was thinking:
thrice -> f(f(f(x))),
let new_x = twice(x)
thrice -> f(f(new_x)),
let new_x2 = twice(new_x)
thrice -> f(new_x2),
let new_thrice = twice(new_x2)
so afterwards I add in the (once) and did
new_thrice(once)(lambda x: x+2)(9)
But answer seems to be that (once) nullifies the earlier thrice(twice) and am lost about. Would be great if someone has an explanation.. Thanks!
I hope this will help you to figure out what is going on!
once = lambda f: lambda x: f(x)
twice = lambda f: lambda x: f(f(x))
thrice = lambda f: lambda x: f(f(f(x)))
# Created this one to help readability.
custom_func = lambda x: x + 2
print("once:", once(custom_func)(9)) # returns value
print("twice:", twice(custom_func)(9)) # returns value
print("thrice:", thrice(custom_func)(9)) # returns value
print("applying all those:", thrice(custom_func)(twice(custom_func)(once(custom_func)(9))))
# This represents the following: (((9 + 2) + 2 + 2) + 2 + 2 + 2)
# each pair of parenthesis mean one function being applied, first once, then twice, then thrice.
# If I've understood correctly you need to achieve 25
# to achieve 25 we need to apply +4 in this result so, which means +2 +2, twice function...
print("Achieving 25:", twice(custom_func)(thrice(custom_func)(twice(custom_func)(once(custom_func)(9)))))
# That is it! Hope it helps.
once(lambda x: x+2) evaluates to a function that applies lambda x: x+2 to its argument. In other words, it's equivalent to lambda x: x+2.
once(once(lambda x: x+2)) evaluates to a function that applies once(lambda x: x+2) to its argument. In other words, it's also equivalent to lambda x: x+2.
once(once(once(lambda x: x+2))) evaluates to a function that applies once(once(lambda x: x+2)) to its argument. In other words, this is also equivalent to lambda x: x+2. This doesn't change no matter how many times you apply once.
thrice(twice)(once) evaluates to a function that applies once to its argument some number of times. (8 times, not that it matters for the analysis.) once doesn't change a function's behavior. No matter how many times you apply once, the final function only applies the underlying function once.
thrice(twice)(once)(lambda x: x + 2) thus evaluates to a function that does the same thing as lambda x: x + 2.
Now, if it had been thrice(twice)(once(lambda x: x + 2)) (note the moved parentheses), then that would have applied thrice(twice) to once(lambda x: x + 2), and the result would be a function that applies lambda x: x + 2 8 times.

how is this lambda function retrieving its x value?

I don't understand how this lambda function knows x is equal to 1?
def one(f = None): return 1 if not f else f(1)
def two(f = None): return 2 if not f else f(2)
def plus(y): return lambda x: x+y
one(plus(two()))
3
I know that the inner function two() returns 2 because f is defaulted to None. Thus y= 2. But how does the lambda function know to look to the outmost function for the x value?
plus returns a (lambda) function. That function is passed to one. Within the scope of one, it is called f.
Then f (which is actually the lambda from returned from plus) is called in f(1).
In other words, the code one(plus(two())) does this:
number2 = two()
lambda_function = plus(number2)
result = one(lambda_function)
If you look at one() it passes "1" into a function which you pass in the arguments (if a function is passed. otherwise 1 is returned). Thus, it evaluates to f(1) (see in the else of one). The function you pass to one() is lambda x: x + 2 (since y=2). Thus, this evaluates to lambda 1: 1 + 2
If you call one(lambda x: 50), it returns 50.
Let's run through the steps first:
one gets a value, f, as plus(two()).
plus gets a value as two, two is gonna be 2 since no f.
Okay, so since one gets the value of that, it gonna condition and see that it shouldn't return 1, so does f(1) the f is the unfinished lambda process, the lambda needs one parameter more to add up, so it got 1, so 2 + 1 is 3.
That is the whole process really.

Function that is sum of arbitrary many other functions

I'm trying to write a function in Python for a polynomial p that is a linear combination of n basis functions phi_i. How can I define a function that is itself a sum of n other functions?
I know that this works:
phi1 = lambda x: x**2
phi2 = lambda x: x
p = lambda x: phi1(x) + phi2(x)
But if I try a loop like this:
p = lambda x: 0
for i in range(0,n):
p = lambda x: p(x)+phi[i](x)
where phi is a list of my basis functions, I create an infinite loop.
I checked Writing a function that is sum of functions, but unfortunately that's not in Python.
You can do this by passing a simple generator expression to sum:
def sigma(funcs, x):
return sum(f(x) for f in funcs)
phi = [lambda x: x**2, lambda x: x]
y = sigma(phi, x)
BTW, it's considered bad style to use lambda for named functions, it's supposed to be for anonymous functions.
If you want a function that doesn't need phi to be passed in each time you call it, there are a couple of ways to do that. The easiest way is to simply use phi in the function. Eg,
def sigma(x):
return sum(f(x) for f in phi)
However, that has a couple of downsides. It won't work if phi isn't in the scope where you call sigma; you can get around that by making phi global, but that may not be convenient, and it's best to avoid globals when they aren't necessary. The other downside is that it uses the current contents of phi, not the contents it had when sigma was defined, so if you modify the contents of phi those changes will be reflected in sigma, which may or may not be desirable.
Another option is to use a closure to create the function. Then we won't be affected by the scope issue: you can call the resulting summing function inside a scope where the original function list isn't visible. We can also create a copy of the function list, so it won't be affected by changes to the passed-in function list.
def make_adder(funcs):
# Copy the function list
funcs = funcs[:]
def sigma(x):
return sum(f(x) for f in funcs)
return sigma
phi = [lambda x: x**2, lambda x: x]
sigma = make_adder(phi)
y = sigma(x)
Yet another option is to use my original sigma and pass it and the phi functions to functools.partial, eg
from functools import partial
sig = partial(sigma, phi)
y = sig(x)
Straight answer to OP
Store your phis in a list:
phis = [
lambda x: x**2,
lambda x: x,
]
p = lambda x: sum(phi(x) for phi in phis)
Further considerations
If you want to achieve a polynomial, I would suggest something similar to this:
def poly(c):
return lambda x: sum(f(x) for f in [lambda x, i=i: c[i]*x**i for i in range(len(c))])
poly function accepts a sequence as the only argument, where its elements need to be int or float. The first element is assigned as the coeficient of x^0, the second to x^1 and so on. So your example (p(x) = x + x^2) would end up being constructed like this: p = poly([0, 1, 1])
Another option is to accept any number of arguments where each of them needs to be a int or float instead of the first being a sequence. This would only require to add one * to the function declaration.
def poly(*c):
return lambda x: sum(f(x) for f in [lambda x, i=i: c[i]*x**i for i in range(len(c))])
To construct your example with this function you would not require the list: p = poly(0, 1, 1).
Any of those methods would create a polynomic function that can be called as you would expect: p(1) would return 2, p(2) would return 6 and so on.
Function explained
def poly(c):
# Create a list where we are gonna store the functions for every term in the polynomial
terms = []
# Create as many terms as the arguments length
for i in range(len(c)):
# Each term is the product of the nth coefficient from c and x to the power of n
terms.append(lambda x, n=i: c[n]*x**n)
# The second parameter n is needed because if we pass i directly
# inside the function algorithm, Python wouldn't check its value
# inmediately, but when it gets executed, and by then the loop will
# be finished and i would be constant for every term. This argument
# is not exposed to the polynomial function as the next lambda only
# has one argument, so there is no way to wrongly call this function
# We return a function that adds the result of every term
return lambda x: sum(f(x) for f in terms)

Categories

Resources