【问题标题】:Can a lambda function call itself recursively in Python?lambda 函数可以在 Python 中递归调用自身吗?
【发布时间】:2010-10-03 15:50:37
【问题描述】:

常规函数可以在其定义中包含对自身的调用,这没问题。我不知道如何使用 lambda 函数来做这件事,但原因很简单,lambda 函数没有名称可以引用。有没有办法做到这一点?怎么样?

【问题讨论】:

  • 我很想标记这个到底是什么,或者你不想做这个。为什么不直接使用普通函数?
  • 我想做的是在树上运行reduce()。 lambda 在 1-D 列表上工作得很好,递归感觉就像是让它在树上工作的自然方式。也就是说,真正的原因是我只是在学习 Python,所以我在踢轮胎。
  • Reduce 与命名函数配合得很好。 Guido 有一段时间想从语言中删除 lambda 表达式。它们幸存了下来,但您仍然没有理由在任何情况下需要使用它们。
  • 请不要使用reduce。使用递归函数减少是非常复杂的。这将需要永远。我认为是 O(n**3) 什么的
  • @S.Lott 无赖。这是 Python 解释器的问题还是我还不理解的更基本的问题?

标签: python recursion lambda y-combinator


【解决方案1】:

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

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]

所以这是可能的,但不是很推荐!

【讨论】:

  • 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))
  • 没用又好玩。这就是我喜欢计算的原因。
  • FWIW,这里是如何使用相同的技术生成斐波那契数列中的数字(为其命名):fibonacci = lambda n: 0 if n == 0 else 1 if n == 1 else fibonacci(n-1)+fibonacci(n-2)
  • 另一种使用 lambdas 和 recusivity 对斐波那契数进行分类的方法:f = lambda x: 1 if x in (1,2) else f(x-1)+f(x-2)
  • 有人能说明为什么“不推荐”执行递归匿名函数调用
【解决方案2】:

没有 reduce、map、命名 lambda 或 python 内部:

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

【讨论】:

  • 由于第一个函数及其返回值被立即调用,它们只是作为赋值。 Python化了一点,这段代码说a = lambda myself, x: 1 if x==0 else x * myself(myself, x-1)然后v = 10最后a(a, v)。复杂的 lambda 被设计为接受自己作为它的第一个参数(因此我将参数重命名为 myself),它使用它来递归调用自己
  • 我利用新的 Python 语法 := 发布了一个新答案,它给出了更短且更具可读性的 (f:=lambda x: 1 if x == 0 else x*f(x - 1))(5)
【解决方案3】:

与你所说的相反,你可以直接这样做。

(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)

第一部分是fixed-point combinatorY,它促进了 lambda 演算中的递归

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

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

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

Y 应用于 fact 以形成另一个 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

但是,如果您更喜欢 fibs 而不是 facts,您也可以使用相同的组合器来做到这一点

>>> (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

【讨论】:

  • 干得好,你刚刚把 Python 变成了 Lisp :)
  • 我利用新的 Python 语法 := 发布了一个新答案,它给出了更短且更具可读性的 (f:=lambda x: 1 if x == 0 else x*f(x - 1))(5)
【解决方案4】:

你不能直接做,因为它没有名字。但是使用像 Lemmy 指出的 Y 组合器这样的辅助函数,您可以通过将函数作为参数传递给自身来创建递归(听起来很奇怪):

# 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))

这会打印前十个斐波那契数:[1, 1, 2, 3, 5, 8, 13, 21, 34, 55],

【讨论】:

  • 我想我终于明白 Y 组合器的用途了。但我认为在 Python 中,通常只使用“def”并给函数命名会更容易......
  • 有趣的是,您的斐波那契示例是使用生成器更自然地完成的一个很好的示例。 :-)
  • 比“否”更好的答案。
  • +1,这是我能理解的唯一答案,不包括不可能函数必须有名字才能称呼自己
  • 其实可以的!我得到了帮助,我们想通了! :) 见:stackoverflow.com/questions/70714874/…
【解决方案5】:

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

(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)

【讨论】:

  • 我不懂 Python,但这看起来很糟糕。确实有更好的方法。
  • nobody - 关键是这看起来很可怕是有原因的。 Python 不是为它设计的,而且它是不好的做法(在 Python 中)。 Lambda 受设计限制。
  • 是的,+1 表示有史以来最糟糕的 Python 代码。当 Perl 人说“如果你知道自己在做什么,你可以用 Perl 编写可维护的代码”时,我会说“是的,如果你知道自己在做什么,你可以用 Python 编写不可维护的代码”。 :-)
  • 我认为用python编写混淆代码是不可能的。事实证明我错了。谢谢我的朋友
  • @habnabit 感谢您的代码!如果我理解得很好,这段代码会从编译的代码中复制 lambda 对象。恕我直言,找到原始对象是一种“更好”的方式。见:stackoverflow.com/questions/70714874/…
【解决方案6】:

这个答案很基础。比 Hugo Walter 的回答简单一点:

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

雨果·沃尔特的回答:

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

【讨论】:

    【解决方案7】:
    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))
    

    希望对某人有所帮助。

    【讨论】:

      【解决方案8】:

      顺便说一下,斐波那契算不算慢:

      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)
      

      【讨论】:

      • 只要你加快速度,就不要使用递归。线性递归比树好,但你仍然会用一个相对较小的参数溢出堆栈。
      【解决方案9】:

      嗯,不完全是纯粹的 lambda 递归,但它适用于只能使用 lambda 的地方,例如减少、映射和列表推导,或其他 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) 中定义的名称。这个例子比较复杂,但它显示了这个概念。

      【讨论】:

        【解决方案10】:

        为此,我们可以使用Fixed-point combinators,特别是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

        另请参阅: Fixed-point combinators in JavaScript: Memoizing recursive functions

        【讨论】:

        • 问题是关于 Python 的。
        • @ruohola 这是所有语言的通用解决方案,但示例是用 js 编写的
        【解决方案11】:

        我们现在可以使用新的 Python 语法来使其更短、更易于阅读:

        斐波那契:

        >>> (f:=lambda x: 1 if x <= 1 else f(x - 1) + f(x - 2))(5)
        8
        

        阶乘:

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

        我们使用:= 来命名 lambda:直接在 lambda 本身中使用该名称并立即将其作为匿名函数调用。

        (见https://www.python.org/dev/peps/pep-0572

        【讨论】:

          【解决方案12】:

          我知道这是一个旧线程,但它在一些谷歌搜索结果中排名很高:)。随着 python 3.8 的到来,您可以使用 walrus 运算符来实现语法更少的 Y 组合器!

          fib = (lambda f: (rec := lambda args: f(rec, args)))\
                (lambda f, n: n if n <= 1 else f(n-2) + f(n-1))
          

          【讨论】:

            【解决方案13】:

            就这么简单:

            fac = lambda n: 1 if n <= 1 else n*fac(n-1)
            

            【讨论】:

              【解决方案14】:

              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
              

              【讨论】:

                【解决方案15】:

                如果你真的是自虐狂,你也许可以使用 C 扩展来做到这一点,但这超出了 lambda(未命名、匿名)函数的能力。

                没有。 (对于大多数 no 值)。

                【讨论】:

                • ( > 这超出了 lambda 的能力) --- 不,它没有。 Y 组合器就像是最有名的抽象结构,它做到了这一点,没有任何技巧。
                猜你喜欢
                • 2019-06-20
                • 2013-01-10
                • 1970-01-01
                • 2017-01-13
                • 2022-11-30
                • 2020-12-23
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多