Python: 在求和中使用 Lambda
我正在尝试做以下事情,这个例子代表了我最终想要实现的目标:
yu = lambda x: 0
for i in range(0,5):
yu = lambda x: i + yu(x)
不幸的是,当我执行时,它返回了:
RuntimeError: maximum recursion depth exceeded
当我这样做时:
print yu(0)
这个打印语句应该返回10。
正确的做法是什么呢?
5 个回答
考虑到你想要把0、1、2、3、4这些数字加起来。如果这样做是对的,你可以使用下面这个简单的表达式:
yu = lambda x: x + yu(x+1) if x<4 else x
当你输入yu(0)
时,它会返回10作为结果。这里有一个停止的条件,就是x
的值必须小于4,才能继续加起来。
如果这正是你想要的结果,那么你可以省略掉循环,这样会让代码更简洁。
这个简单的表达式和其他的不同,因为它会根据你传入的参数返回不同的值(除了当你选择0作为参数x时,它总是返回10)。
最后,你得到了:
yu = lambda x: i + yu(x)
但是,yu
会在程序运行时被查找,而不是在你创建这个 lambda 时查找。你应该这样做:
for i in range(0,5):
yu = lambda x, yu=yu: i + yu(x)
不过,这样做并不会返回 10
,而是返回 20
:
>>> yu = lambda x: 0
>>> for i in range(0,5):
... yu = lambda x, yu=yu: i + yu(x)
...
>>> yu(0)
20
因为现在 i
还是从上下文中查找(而此时循环已经结束,所以它的值是 4
)。解决办法?把 i
也作为一个关键字参数:
for i in range(0,5):
yu = lambda x, yu=yu, i=i: i + yu(x)
现在这样就可以了:
>>> yu = lambda x: 0
>>> for i in range(0,5):
... yu = lambda x, yu=yu, i=i: i + yu(x)
...
>>> yu(0)
10
这个故事的道理是什么呢?要正确地将你的上下文绑定到 lambda 的作用域中。
yu = lambda x: i + yu(x)
这段话的意思是,yu
变成了一个总是调用自己的函数,这样就会一直重复下去,形成无限循环,而没有一个结束的条件。
为什么会这样呢?因为你创建了一个闭包,其中yu
和i
是这个函数或模块中for
循环的局部变量。这样并不是你想要的;你应该使用当前的值,而不是外部的变量。
我不太明白你为什么一开始就要用lambda
。如果你想定义一个函数并给它起个名字,直接用def
就可以了。
这里有一个简单的解决办法:
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)
通过明确地包装,正确的做法就会变得非常明显。
当然,你可以在make_new_yu
里面使用lambda
,这样不会让事情变得更复杂:
def make_new_yu(yu, i):
return lambda x: i + yu(x)
如果你愿意,最开始的定义也可以用lambda
。但是如果你坚持不使用任何的def
语句,你就需要以某种方式把正确的值放入闭包中,比如使用默认值的技巧。这种方法更容易出错,而且一旦写了之后也更难阅读。
如果你想直观地理解它们之间的区别,而不想了解太多细节:函数体定义了一个新的作用域。所以,在这个函数内部定义函数(无论是用lambda
还是def
)意味着你的闭包是来自那个新的作用域。