Lambda函数可以在Python中递归调用吗?


71

常规函数可以在其定义中包含对自身的调用,没问题。我无法弄清楚如何使用lambda函数,原因很简单,因为lambda函数没有可引用的名称。有办法吗?怎么样?


2
我很想为这个标签,或者你不想做这个。您为什么不只使用普通功能?
phihag

5
我要在树上运行reduce()。Lambda在一维列表中效果很好,而递归感觉就像是使其在树上工作的自然方式。就是说,真正的原因是我只是在学习Python,所以我不知所措。

Reduce可以很好地使用命名函数。Guido希望暂时从该语言中删除lambda表达式。它们幸存了下来,但是仍然没有理由在任何情况下都需要使用它们。
John Fouhy

1
请不要使用reduce。用递归函数减少是疯狂的复杂。这将永远。我认为是O(n ** 3)之类的东西
S.Lott

1
@ S.Lott讨厌 这是Python解释器的问题,还是我还不了解的更基本的问题?

Answers:


73

我能想到的唯一方法就是为函数命名:

fact = lambda x: 1 if x == 0 else x * fact(x-1)

或者,对于早期版本的python:

fact = lambda x: x == 0 and 1 or x * fact(x-1)

更新:使用其他答案中的想法,我能够将阶乘函数嵌入单个未命名的lambda中:

>>> map(lambda n: (lambda f, *a: f(f, *a))(lambda rec, n: 1 if n == 0 else n*rec(rec, n-1), n), range(10))
[1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880]

因此有可能,但不建议这样做!


2
map(lambda n: (lambda f, n: f(f, n))(lambda f, n: n*f(f, n-1) if n > 0 else 1, n), range(10))
jfs

2
无用而有趣。这就是为什么我喜欢计算。
e-satis

2
FWIW,这里是如何使用相同的技术生成斐波那契数列(分配给它一个名称)fibonacci = lambda n: 0 if n == 0 else 1 if n == 1 else fibonacci(n-1)+fibonacci(n-2)
martineau 2010年

使用lambda和回溯性生成斐波那契数的另一种方式: f = lambda x: 1 if x in (1,2) else f(x-1)+f(x-2)
Juan Gallostra 2013年

1
有人可以说明为什么不建议进行递归匿名函数调用的原因吗?
ThorSummoner14年

58

没有reduce,map,名为lambdas或python内部的:

(lambda a:lambda v:a(a,v))(lambda s,x:1 if x==0 else x*s(s,x-1))(10)

2
由于第一个函数及其返回值会立即被调用,因此它们仅用作分配。Pythonized了一下,这个代码表示,a = lambda myself, x: 1 if x==0 else x * myself(myself, x-1)那么v = 10最后a(a, v)。复杂的lambda旨在接受自身作为其第一个参数(因此,为什么我将参数重命名为myself),该参数用于递归地调用自身
Felipe

29

与某事相反,您可以直接执行此操作。

(lambda f: (lambda x: f(lambda v: x(x)(v)))(lambda x: f(lambda v: x(x)(v))))(lambda f: (lambda i: 1 if (i == 0) else i * f(i - 1)))(n)

第一部分是定点组合器 Y,它有助于lambda演算中的递归

Y = (lambda f: (lambda x: f(lambda v: x(x)(v)))(lambda x: f(lambda v: x(x)(v))))

第二部分是递归定义的阶乘函数事实

fact = (lambda f: (lambda i: 1 if (i == 0) else i * f(i - 1)))

Y应用于事实以形成另一个lambda表达式

F = Y(fact)

这适用于第三部分n,后者可忽略为第n个阶乘

>>> n = 5
>>> F(n)
120

或同等

>>> (lambda f: (lambda x: f(lambda v: x(x)(v)))(lambda x: f(lambda v: x(x)(v))))(lambda f: (lambda i: 1 if (i == 0) else i * f(i - 1)))(5)
120

然而,如果你喜欢小谎的事实,你可以做到这一点使用相同的组合子

>>> (lambda f: (lambda x: f(lambda v: x(x)(v)))(lambda x: f(lambda v: x(x)(v))))(lambda f: (lambda i: f(i - 1) + f(i - 2) if i > 1 else 1))(5)
8

14
干得好,您刚刚将Python变成Lisp :)
Eliza Weisman 2014年

23

您无法直接执行此操作,因为它没有名称。但是使用Y-combinator Lemmy指出的辅助函数,您可以通过将函数作为参数传递给自身来创建递归(听起来很奇怪):

# helper function
def recursive(f, *p, **kw):
   return f(f, *p, **kw)

def fib(n):
   # The rec parameter will be the lambda function itself
   return recursive((lambda rec, n: rec(rec, n-1) + rec(rec, n-2) if n>1 else 1), n)

# using map since we already started to do black functional programming magic
print map(fib, range(10))

这将打印前十个Fibonacci数:[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]


1
我想我终于了解了Y组合器的用途。但是我认为,在Python中,通常只使用“ def”并给函数命名就更容易了……
pdc

有趣的是,您的斐波那契示例是发电机自然完成的一个很好的示例。:-)
pdc

答案比“否”要好得多。
new123456 2011年

1
+1,这是我能理解的唯一答案,但不包括这些说法,这是不可能的,并且该函数必须具有自己的名称
GingerPlusPlus 2014年

11

是。我有两种方法可以做到,一种已经被涵盖。这是我的首选方式。

(lambda v: (lambda n: n * __import__('types').FunctionType(
        __import__('inspect').stack()[0][0].f_code, 
        dict(__import__=__import__, dict=dict)
    )(n - 1) if n > 1 else 1)(v))(5)

9
我不了解Python,但这看起来很糟糕。确实有更好的方法。
凯尔·克罗宁

没有人-关键是这看起来很可怕是有原因的。Python不是为它设计的,这是不正确的做法(在Python中)。Lambda受设计限制。
格雷格·林德

19
是的,+ 1是有史以来最糟糕的Python代码。当Perl人们说“如果您知道自己在做什么,就可以在Perl中编写可维护的代码”,我说:“是的,如果您知道自己在做什么,就可以在Python中编写不可维护的代码”。:-)
Lennart Regebro

我认为用python编写混淆代码是不可能的。我被证明是错的。谢谢我的朋友
antimatter 2014年

5

这个答案很基本。它比Hugo Walter的答案要简单一些:

>>> (lambda f: f(f))(lambda f, i=0: (i < 10)and f(f, i + 1)or i)
10
>>>

雨果·沃尔特(Hugo Walter)的答案:

(lambda a:lambda v:a(a,v))(lambda s,x:1 if x==0 else x*s(s,x-1))(10)


2
def recursive(def_fun):
    def wrapper(*p, **kw):
        fi = lambda *p, **kw: def_fun(fi, *p, **kw)
        return def_fun(fi, *p, **kw)

    return wrapper


factorial = recursive(lambda f, n: 1 if n < 2 else n * f(n - 1))
print(factorial(10))

fibonaci = recursive(lambda f, n: f(n - 1) + f(n - 2) if n > 1 else 1)
print(fibonaci(10))

希望对某人有帮助。


1

顺便说一句,而不是缓慢地计算斐波那契:

f = lambda x: 1 if x in (1,2) else f(x-1)+f(x-2)

我建议快速计算斐波那契:

fib = lambda n, pp=1, pn=1, c=1: pp if c > n else fib(n, pn, pn+pp, c+1)

它的工作速度非常快。

这里也是阶乘计算:

fact = lambda n, p=1, c=1: p if c > n else fact(n, p*c, c+1)

0

好吧,不完全是纯lambda递归,它适用于只能使用lambda的地方,例如,reduce,map和list comprehensions或其他lambda。诀窍是从列表理解和Python的名称范围中受益。下面的示例通过给定的键链遍历字典。

>>> data = {'John': {'age': 33}, 'Kate': {'age': 32}}
>>> [fn(data, ['John', 'age']) for fn in [lambda d, keys: None if d is None or type(d) is not dict or len(keys) < 1 or keys[0] not in d else (d[keys[0]] if len(keys) == 1 else fn(d[keys[0]], keys[1:]))]][0]
33

lambda重用在列表理解表达式(fn)中定义的名称。该示例相当复杂,但它显示了概念。


0

为此,我们可以使用定点组合器,特别是Z组合器,因为它可以在严格的语言(也称为急切语言)中工作:

const Z = f => (x => f(v => x(x)(v)))(x => f(v => x(x)(v)))

定义fact函数并对其进行修改:

1. const fact n = n === 0 ? 1 : n * fact(n - 1)
2. const fact = n => n === 0 ? 1 : n * fact(n - 1)
3. const _fact = (fact => n => n === 0 ? 1 : n * fact(n - 1))

注意:

事实=== Z(_fact)

并使用它:

const Z = f => (x => f(v => x(x)(v)))(x => f(v => x(x)(v)));

const _fact = f => n => n === 0 ? 1 : n * f(n - 1);
const fact = Z(_fact);

console.log(fact(5)); //120

另请参见: JavaScript中的定点组合器:记忆递归函数


问题是关于Python。
ruohola

-1

Lambda可以轻松替换Python中的递归函数:

例如,以下基本的compound_interest:

def interest(amount, rate, period):
    if period == 0: 
        return amount
    else:
        return interest(amount * rate, rate, period - 1)

可以替换为:

lambda_interest = lambda a,r,p: a if p == 0 else lambda_interest(a * r, r, p - 1)

或提高可见度:

lambda_interest = lambda amount, rate, period: \
amount if period == 0 else \
lambda_interest(amount * rate, rate, period - 1)

用法:

print(interest(10000, 1.1, 3))
print(lambda_interest(10000, 1.1, 3))

输出:

13310.0
13310.0

-3

如果您确实是受虐狂,则可以使用C扩展名来做到这一点,但要回显Greg(嗨,Greg!),这超出了lambda(未命名,匿名)功能的功能。

否(对于大多数否定值)。


5
(>这超出了lambda的功能)---不,不是。Y组合器就像是最著名的抽象构造,它确实做到了这一点。
Danny Milosavljevic
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.