递归的lambda表达式可能吗?

19 投票
6 回答
6731 浏览
提问于 2025-04-15 13:16

我正在尝试写一个可以自我调用的lambda表达式,但我找不到任何相关的语法,甚至不知道这是否可能。

其实我想把下面这个函数转换成一个lambda表达式:(我知道这听起来有点傻,因为它只是做加法,但我想探索一下在Python中可以用lambda表达式做些什么)

def add(a, b):
   if a <= 0:
      return b
   else:
      return 1 + add(a - 1, b)

add = lambda a, b: [1 + add(a-1, b), b][a <= 0]

但是调用这个加法的lambda形式时,会出现运行时错误,因为达到了最大递归深度。这样做在Python中真的可行吗?还是我只是犯了什么愚蠢的错误?哦,我使用的是Python 3.0,但我觉得这应该没关系吧?

6 个回答

6

首先,递归的 lambda 表达式其实是没必要的。正如你自己提到的,lambda 表达式要调用自己,就必须有个名字。但 lambda 表达式就是匿名函数,所以如果你给它起了名字,那就不再是 lambda 表达式了,而是一个普通的函数。

因此,使用 lambda 表达式是没什么用的,只会让人困惑。所以还是用 def 来定义函数吧。

不过,正如你自己发现的,lambda 表达式是可以递归的。你给出的例子就是一个。实际上,它递归得非常厉害,以至于超过了最大递归深度。所以确实是递归的。你遇到的问题是你在表达式中总是调用 add,这样递归就永远不会停止。不要这样做。你的表达式可以这样写:

add = lambda a, b: a > 0 and (1 + add(a-1, b)) or b

这样就解决了这个问题。不过,你最开始的 def 写法才是正确的做法。

9

也许你可以试试这个叫做 Z组合子 的东西,这个例子就是来自这里的:

>>> Z = lambda f: (lambda x: f(lambda *args: x(x)(*args)))(lambda x: f(lambda *args: x(x)(*args)))
>>> fact = lambda f: lambda x: 1 if x == 0 else x * f(x-1)
>>> Z(fact)(5)
120
20

也许你需要一个Y组合子?

编辑 - 其实应该是Z组合子(我之前没意识到Y组合子更适合按名称调用)

这里用的是来自维基百科的Z组合子的定义

>>> Z = lambda f: (lambda x: f(lambda *args: x(x)(*args)))(lambda x: f(lambda *args: x(x)(*args)))

使用这个,你可以将加法定义为一个完全匿名的函数(也就是说,在它的定义中没有提到它的名字)

>>> add = Z(lambda f: lambda a, b: b if a <= 0 else 1 + f(a - 1, b))
>>> add(1, 1)
2
>>> add(1, 5)
6

撰写回答