【问题标题】:Why does wrapping a function with a lambda fix stack overflow?为什么用 lambda 包装函数修复堆栈溢出?
【发布时间】:2023-03-03 01:12:01
【问题描述】:

如果 expression 计算结果为一个参数的函数,我会认为 lambda x: (expression)(x) 与 expression em> - 但实际上并非如此。考虑 Y 组合子的以下两个定义:

def Y1(F):
    left = lambda x: x(x)
    right = lambda x: F(x(x))
    return left(right)

def Y2(F):
    left = lambda x: x(x)
    right = lambda x: lambda y: (F(x(x)))(y)
    return left(right)

Y2 实际上按预期工作,但调用 Y1 会引发堆栈溢出。为什么会有这种行为差异?

【问题讨论】:

  • 一个函数通过一个参数泛化表达式从一个表达式中抽象出来。这样的表达式仅在提供参数时才被评估,即它被延迟评估。添加一个冗余的 lambda 函数正是这种效果。这种技术称为eta abstraction,用于严格评估的语言中以引入惰性。

标签: python lambda functional-programming stack-overflow y-combinator


【解决方案1】:

不,lambda x: (expression)(x)expression 不同。

这是一个在调用时返回expression结果的函数,不像expression会立即返回其结果。

结果如何?它是一个参数的函数。但它还不存在。它需要构建,计算。这就是expression 正在做的事情。它计算代表“下一个”递归调用的递归函数,可能需要由 Y 组合器构造的递归函数完成

Y1 试图过早、太快、太急切地找到right 的值——它试图在返回计算的递归函数之前这样做。因此永远不会返回,因为Y1 总是在返回前一个递归函数之前尝试计算下一个递归函数,以便可以调用它。

但是Y2构造了递归函数,在需要it时计算下一个递归函数;但还没有,不是“现在”。它将其构造为 lambda 函数,它延迟实际计算。 lambda 函数的构造是一个简单的一步过程,快速完成,然后返回计算出的递归函数,因此可以使用它——并且只有当/当 it 确定下一个需要执行层级的递归调用,它会在那个时间点调用那个lambda来构造那个那个时间点的下一个递归函数 em>,即在调用它之前。

不像Y1 试图做的那样提前方式

【讨论】:

    猜你喜欢
    • 2012-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-09
    • 1970-01-01
    • 1970-01-01
    • 2021-06-18
    • 1970-01-01
    相关资源
    最近更新 更多