函数递归在for循环中不起作用,但在外部起作用。

huangapple go评论48阅读模式
英文:

Function recursion not working in for loop but working outside

问题

让我们考虑以下代码:

def f(x):
    return x

sol = [f]
sol.append(lambda x: sol[0](x) + x)
sol.append(lambda x: sol[1](x) + x)

print(sol[0](1), sol[1](1), sol[2](1))
# 1 2 3

以这种方式做,我得到了我想要的结果。现在,让我们使用for循环完全相同的方式来做一遍:

def f(x):
    return x

sol = [f]
for i in range(1, 3):
    sol.append(lambda x: sol[i](x) + x)

print(sol[0](1), sol[1](1), sol[2](1))
# RecursionError: maximum recursion depth exceeded.

有人可以解释一下发生了什么吗?

乍一看,我认为问题类似于这个问题:lambda函数重复调用自身,直到达到递归限制。但我不明白为什么会出现这种情况。

英文:

Let's consider the following code :

def f(x):
    return x

sol = [f]
sol.append(lambda x: sol[0](x) + x)
sol.append(lambda x: sol[1](x) + x)

print(sol[0](1), sol[1](1), sol[2](1))
# 1 2 3

Doing in such a way I got what I want. Now, let's do the exact same thing but with a for loop :

def f(x):
    return x

sol = [f]
for i in range(1, 3):
    sol.append(lambda x: sol[i](x) + x)

print(sol[0](1), sol[1](1), sol[2](1))
# RecursionError: maximum recursion depth exceeded.

Can someone explain me what's happening ?

At first glance, I thought that the problem was similar to that one : lambda function calls itself repeatedly until it bottoms out at the recursion limit. But I do not see why that could be the case.

答案1

得分: 3

Your code creates a closure, so that when the lambdas are called, they do not use the value of i that was in effect at the time they were created, but instead they use the current value of i, which is 2. So it's equivalent to:

sol = [f]
sol.append(lambda x: sol[2](x) + x)
sol.append(lambda x: sol[2](x) + x)

To fix it, you need to capture the value of the loop index at the time the lambda is created. This can be done by lambda-binding the loop value (see the solutions below).

The other problem with the original code is that the loop index runs from 1 to 2, when in fact it needs to from from 0 to 1 (so that the lambdas refer to the previous functions in the list rather than to themselves).

Both problems can be fixed as follows:

sol = [f]
for i in range(0, 2):
    sol.append((lambda i: lambda x: sol[i](x) + x)(i))

This produces the desired behavior. There's also a trick you can use to make it more concise:

sol = [f]
for i in range(0, 2):
    sol.append(lambda x, i=i: sol[i](x) + x)

This binds i as the default value of an optional second argument. The only difference in behavior is that the second argument can now be specified by the caller, which you may or may not want.

英文:

Your code creates a closure, so that when the lambdas are called, they do not use the value of i that was in effect at the time they were created, but instead they use the current value of i, which is 2. So it's equivalent to:

sol = [f]
sol.append(lambda x: sol[2](x) + x)
sol.append(lambda x: sol[2](x) + x)

To fix it, you need to capture the value of the loop index at the time the lambda is created. This can be done by lambda-binding the loop value (see the solutions below).

The other problem with the original code is that the loop index runs from 1 to 2, when in fact it needs to from from 0 to 1 (so that the lambdas refer to the previous functions in the list rather than to themselves).

Both problems can be fixed as follows:

sol = [f]
for i in range(0, 2):
    sol.append((lambda i: lambda x: sol[i](x) + x)(i))

This produces the desired behavior. There's also a trick you can use to make it more concise:

sol = [f]
for i in range(0, 2):
    sol.append(lambda x, i=i: sol[i](x) + x)

This binds i as the default value of an optional second argument. The only difference in behavior is that the second argument can now be specified by the caller, which you may or may not want.

huangapple
  • 本文由 发表于 2023年5月30日 02:18:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/76359559.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定