Eli
Eli

Reputation: 43

Python: Lambda in a sum

I'm trying to do the following, which is a representative example of what my final goal will be:

 yu = lambda x: 0
 for i in range(0,5):
      yu = lambda x: i + yu(x)

Unfortunately, it returns:

RuntimeError: maximum recursion depth exceeded

when I do:

print yu(0)

The print statement should return 10.

What's the correct way to do this?

Upvotes: 2

Views: 9947

Answers (5)

Dennis
Dennis

Reputation: 1951

Considering the answers you try to sum up 0, 1, 2, 3, 4. If that was right you could use the following lambda expression:

yu = lambda x: x + yu(x+1) if x<4 else x

For yu(0) it delivers 10 as a result. The break condition is the value of x which is required to stay smaller than 4 in order to add up.

Assuming that is what you desired to do, you should leave out the loop for a rather concise statement.

This lambda expression differentiates from the others in resulting in different values (other than 10, when not choosing 0 as the parameter x) depending on the given argument.

Upvotes: 0

user3233919
user3233919

Reputation: 45

facto = lambda f: f if f == 0 else f + facto(f-1)
print(facto(4))

Upvotes: 0

StickySli
StickySli

Reputation: 101

I believe I didn't fully understand your question, but could anyone check if this is what he meant?

I assumed you wouldn't need an iterator to generate a list of digits

yu = lambda x: x[0] + yu(x[1:]) if x!=[] else 0

Upvotes: 0

Martijn Pieters
Martijn Pieters

Reputation: 1122232

In the end, you have:

yu = lambda x: i + yu(x)

but yu will be looked up at runtime, not when you constructed the lambda. Do this instead:

for i in range(0,5):
    yu = lambda x, yu=yu: i + yu(x)

This does not return 10, though. It returns 20 instead:

>>> yu = lambda x: 0
>>> for i in range(0,5):
...     yu = lambda x, yu=yu: i + yu(x)
... 
>>> yu(0)
20

because now i is still looked up from the context (and by now the loop has finished so it's 4). Solution? Move i to a keyword argument too:

for i in range(0,5):
    yu = lambda x, yu=yu, i=i: i + yu(x)

Now this works:

>>> yu = lambda x: 0
>>> for i in range(0,5):
...     yu = lambda x, yu=yu, i=i: i + yu(x)
... 
>>> yu(0)
10

Moral of the story? Bind your context properly to the scope of the lambda.

Upvotes: 5

abarnert
abarnert

Reputation: 365767

 yu = lambda x: i + yu(x)

This makes yu into a function that always calls itself, guaranteeing infinite recursion with no base case.

Why? Well, you've built a closure where yu (and i) are the local variables in the function or module that the for loop is part of. That's not what you want; you want to close over the current values of yu and i, not the outer variables.

I'm not sure why you're even using lambda in the first place. If you want to define a function and give it a name, use def.

Here's an easy solution:

def yu(x): return 0
def make_new_yu(yu, i):
    def new_yu(x): return i + yu(x)
    return new_yu
for i in range(0, 5):
    yu = make_new_yu(yu, i)

By making the wrapping explicit, the correct way to do it becomes the most obvious way to do it.

You can, of course, use a lambda inside make_new_yu without making things more confusing:

def make_new_yu(yu, i):
    return lambda x: i + yu(x)

And you can even make the initial definition a lambda if you want. But if you insist on not having any def statements, you need to force the right values into the closure in some way, e.g., by using the default-value trick. That's much easier to get wrong—and harder to read once you've done it.

If you want an intuitive understanding of the difference, without learning the details: a function body defines a new scope. So, defining the function (by lambda or def) inside that function means your closure is from that new scope.

Upvotes: 2

Related Questions