Kernel restarts when calling self defined function which adds together lambda functions - python

I've tried to define a function that adds several lambda functions together.
When I call the function the kernel crashes and restarts with no further error messages.
def add_lambdas(function_list):
f = lambda x, y: None
for function in function_list:
f = lambda x, y: f(x, y) + function(x, y)
return f
f1 = lambda x, y: x + y
f2 = lambda x, y: x**2 - 2*y
f3 = lambda x, y: f1(x,y) + f2(x,y)
f4 = add_lambdas([f1,f2])
For example, when I call f3(2,3) all is well, but when I call f4(2,3) the kernel crashes and restarts. For f3 I do the adding of f1 and f2 manually, but for f4 I pass them through the function add_lambdas().
This is the output:
In[5]: f3(2,3)
Out[5]: 3
In[6]: f4(2,3)
Restarting kernel...
Obviously something is wrong with my defined function, but I cant figure out what it is. Any thoughts? Thank you!

I believe the problem is that you're defining f as a recursive function, which I don't think is what you want. You can try this instead:
def add_lambdas(function_list):
return lambda x, y: sum(f(x, y) for f in function_list)

You are getting a stack overflow. The problem is that there is only a single variable f and a single variable function in add_lambdas. When you write:
f = lambda x, y: f(x, y) + function(x, y)
you are just assigning a new value to the variable f, but the variable f inside the definition is also that new value. This is the stack overflow. In addition, the variable function will be left with the last value in the list. There is no reason for it to copy the value.
If instead you write:
f = lambda x, y, f=f, function=function: f(x, y) + function(x, y),
you're telling Python to assign f and function to new variables, (with the same name) and to use those new variables inside the lambda. Changes made to the other variables are immaterial
You'll discover a different error in your code. The first line needs to be
f = lambda x, y: 0 since otherwise you're adding an integer to None.
Note that #josemz is the simpler solution if you're just planning on adding things. If you want to learn to use lambdas within other lambdas, you need to make sure variables are bound correctly.

Related

How external variables can be passed into the scope of lambda functions?

I want to define a lambda function inside another lambda function, but when I create the inner lambda function, the outer lambda function's argument is said to be undefined, like this.
# time, game, players... are defined above this line
result = filter(lambda x: (player == min(players, key=lambda y: time(game, y, x))), words)
Then, the x as one of the time function's argument is said to be an undefined variable. I searched through StackOverflow and found that I can use a default value to pass x into the scope of lambda y by changing it to lambda y, x=x: time(game, y, x).
I am wondering why x couldn't be used in the scope of lambda y directly, just like how game, time() are used in it. Aren't game, time, x all variables outside the scope of lambda y? What's the difference between x and these other variables/functions.
Thanks very much!
I am not sure why you are having the problem. In a minimal example like this:
(lambda x: (lambda y: y+x)(3))(2)
You get the expected 5, so there must be something more to what you are doing.

python lambda functions defined in a loop, end up pointing to the same operation

I was looking for an algorithm capable of storing and running functions with a flexible number of arguments. I ended up finding a switch/case analogue for python which meets my requirements:
def opA_2(x, y):
return x - y
def opB_3(x, y, z):
return x + y - z
def opC_2(x, y):
return x * y
def opD_3(x, y, z):
return x * y + z
op_dict = {'opA_2': opA_2,
'opB_3': opB_3,
'opC_2': opC_2,
'opD_3': opD_3
}
op_lambda_dict = {'opA_2': lambda x, y, kwargs: op_dict['opA_2'](x, y),
'opB_3': lambda x, y, kwargs: op_dict['opB_3'](x, y, kwargs['z']),
'opC_2': lambda x, y, kwargs: op_dict['opC_2'](x, y),
'opD_3': lambda x, y, kwargs: op_dict['opD_3'](x, y, kwargs['z']),
}
def dispatch_op(func_dict, op, x, y, **kwargs):
return func_dict.get(op, lambda a, b, c: None)(x, y, kwargs)
coefs_dict = {'i': 1, 'j': 2, 'k': 3, 'z': 4}
print('Original lambda dict result:', dispatch_op(op_lambda_dict, 'opB_3', 1, 2, **coefs_dict))
Resulting in:
Original lambda dict result: -1
Once I implemented this structure to my target code, however, I encountered many issues because my operations are defined via a loop.
As far as I understand it, this is because the lambda functions are not initialised, and they end up pointing to the last operation declared.
This additional code reproduces the issue:
op_looplambda_dict = {}
for label, func in op_dict.items():
if '2' in label:
op_looplambda_dict[label] = lambda x, y, kwargs: func(x, y)
if '3' in label:
op_looplambda_dict[label] = lambda x, y, kwargs: func(x, y, kwargs['z'])
print('Loop lambda dict result:', dispatch_op(op_looplambda_dict, 'opB_3', 1, 2, **coefs_dict))
Resulting in:
Loop lambda dict result: 6
This is the result of opD_3 instead of opB_3
I wonder if anyone could please offer any advice, on how to properly declare the lambda functions in the second case or a different code structure to avoid it. Thanks a lot.
The problem is to do with scope: each lambda function creates a "closure" over the func variable, and can access that variable's current value when the function runs. And because it's not running until the loop is complete, the value it uses is the last one it held in the loop.
The solution is therefore to utilise scope to your advantage, by ensuring that each lambda closes over a func variable which only ever holds one value - the value you need. You need a new scope for each iteration of the loop. And the only way Python allows you to create scope is with a function.
So the solution will look something like this:
op_looplambda_dict = {}
for label, func in op_dict.items():
def add_to_dict(function):
if '2' in label:
op_looplambda_dict[label] = lambda x, y, kwargs: function(x, y)
if '3' in label:
op_looplambda_dict[label] = lambda x, y, kwargs: function(x, y, kwargs['z'])
add_to_dict(func)
I'd be the first to admit that this is a little clumsy, but it should work. The only difference from your code is that I've put the loop body inside a function - then immediately called that function each time. So there's no difference in how it behaves, except for the how the variables are scoped - which is key here. When those lambda function run, they will use the value of function from their immediately enclosing scope, and because off the add_to_dict function having its own scope, that will be a different function for each time through the loop.
I would encourage you to look at this Q&A for useful background - it's about Javascript rather than Python, but the scoping mechanisms (at least in "old-school" Javascript before ES6) are identical between the two languages, and the underlying issue you have here is identical to the one in this much-asked JS question. Unfortunately many of the solutions that are available in modern Javascript aren't applicable to Python, but this one does - a simple adaptation of the "Immediately Invoked Function Expression" (IIFE for short) pattern that's common in JS.
And as you can probably tell, I'm personally more experienced with JS than with Python, so I'd be interested to hear if there's a nicer, more idiomatic, Python solution to this problem than what I've done above.
The functions can be added in the dictionary using their names.
When you dispatch a call to an operation, you could compare the received arguments with signature of matched function in the registry.
This way you could figure out other arguments for that function from the optional arguments passed in the dispatch call. For example,
import inspect
import functools
registry = {}
def register_operation(func):
if func.__name__ in registry:
raise TypeError("duplicate registration")
func.__signature = inspect.signature(func)
registry[func.__name__] = func
return func
def dispatch_operation(func_name, *args, **kwargs):
func = registry.get(func_name, None)
if not func:
raise TypeError("no match")
rest_parameters = list(func.__signature.parameters)[len(args):]
rest_args = (kwargs.get(p) for p in rest_parameters)
ba = func.__signature.bind(*args, *rest_args)
ba.apply_defaults()
return func(*ba.args, **ba.kwargs)
#register_operation
def opA_2(x, y):
return x - y
#register_operation
def opB_3(x, y, z):
return x + y - z
#register_operation
def opC_2(x, y):
return x * y
#register_operation
def opD_3(x, y, z):
return x * y + z
coefs_dict = {'i': 1, 'j': 2, 'k': 3, 'z': 4}
print('Original dict result:', dispatch_operation('opB_3', 1, 2, **coefs_dict))

Why is this different? Currying/Higher Order Functions

I'm wondering why these 2 pieces of code are different?
This code gives me the answer of 5
curry2 = lambda f: lambda x: lambda y: f(x, y)
m = curry2(add)
m(2)(3)
5
This one gives me the location of my function
def lambda_curry2(func):
return lambda f: lambda x: lambda y: f(x, y)
curry2 = lambda_curry2(add)
add_three = curry2(3)
add_three(5)
The second one isn't using func. You don't need as many lambdas, because func is the function you want to call.
So it should be;
def lambda_curry2(func):
return lambda x: lambda y: func(x, y)
Put another way, your definition of curry2 is equivalent to:
def curry2(f):
return lambda x: lambda y: f(x, y)
In general,
name = lambda <vars>: <expression>
is short for
def name(<vars>):
return <expression>
lambda is usually used when you don't need to name the function (e.g. when you want to pass a simple function as an argument or return it as a value).

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)

Lambda, calling itself into the lambda definition

I'm doing a complicated hack in Python, it's a problem when you mix for+lambda+*args (don't do this at home kids), the boring details can be omited, the unique solution I found to resolve the problem is to pass the lambda object into the self lambda in this way:
for ...
lambda x=x, *y: foo(x, y, <selflambda>)
It's possible?, thanks a lot.
You are looking for a fixed-point combinator, like the Z combinator, for which Wikipedia gives this Python implementation:
Z = lambda f: (lambda x: f(lambda *args: x(x)(*args)))(lambda x: f(lambda *args: x(x)(*args)))
Z takes one argument, a function describing the function you want, and builds and returns that function.
The function you're looking to build is:
Z(lambda f: lambda x=x, *y: foo(x, y, f))
While your question is genuinely weird, try something like:
>>> import functools
>>> f = lambda selflambda, x=x, *y: foo(x, y, selflambda)
>>> f = functools.partial(f, f)
If you want to refer to it, you'll have to give it a name
bar=lambda x=x, *y: foo(x, y, bar)
such is the way of the snake
The easiest way is to write a separate function to create the lambda.
def mkfun(foo, x):
f = lambda x=x, *y: foo(x, y, f)
return f
for ...:
...mkfun(foo, x)...
This works just like gnibbler's suggestion but can be used in a for loop.
EDIT: I wasn't joking. It really works!
def foo(x, y, bar):
print x
if y:
bar() # call the lambda. it should just print x again.
# --- gnibbler's answer
funs = []
for x in range(5):
bar=lambda x=x, *y: foo(x, y, bar) # What does bar refer to?
funs.append(bar)
funs[2](2, True) # prints 2 4 -- oops! Where did the 4 come from?
# --- this answer
def mkfun(x, foo):
bar = lambda x=x, *y: foo(x, y, bar) # different bar variable each time
return bar
funs = []
for x in range(5):
funs.append(mkfun(x, foo))
funs[2](2, True) # prints 2 2
I don't understand why you want to do this with lambda.
lambda: creates a function object that does not have a name
def: creates a function object that does have a name
a name: very useful for calling yourself
for ...
def selflambda(x=x, *y):
return foo(x, y, selflambda)
...
Doesn't this do exactly what you requested? I even called it selflambda. If it doesn't do what you want, would you please explain why it doesn't?
EDIT: Okay, Jason Orendorff has pointed out that this won't work, because each time through the loop, the name selflambda will be rebound to a new function, so all the function objects will try to call the newest version of the function. I'll leave this up for the educational value, not because it is a good answer.

Categories

Resources