2

我在思考延迟评估的工作原理时遇到了一些问题。我试图用 Y-Combinator 来理解它:

如果我们编写一个简单版本的 Y-Combinator,我们会遇到无限递归的问题:

Ysimple = (lambda f : (lambda x : f(x(x))) (lambda x : f(x(x))))

当我们构建一个递归函数时,问题就出现了:

almost_factorial = lambda f : lambda n : 1 if n == 0 else n * f(n-1)

factorial = Ysimp(almost_factorial) # <- infinite recursion

Ysimple = (lambda f : (lambda x : f(x(x))) (lambda x : f(x(x)) ))
[上一行重复了 995 次以上] RecursionError: 超出最大递归深度

但是我们可以将第二个或两个 -f(x(x))表达式包装在延迟抽象中:

Ydelay = (lambda f : (lambda x : f(x(x))) (lambda x : f(lambda y: x(x)(y))) )

现在代码工作得很好。但为什么?

如果我们只有Ysimple在我们的文件中,则不会评估任何内容。所以我假设只有 lambdas 被评估为顶级表达式。

我做了一些手动评估步骤,但我没有看到延迟发生的原因:

Ysimple F =  (lambda f : (lambda x : f(x(x))) (lambda x : f(lambda y: x(x)(y)))) F
          -> (lambda x : F(x(x))) (lambda x : F(lambda y: x(x)(y)))
          -> F( (lambda x : F(lambda y: x(x)(y))) (lambda x : F(lambda y: x(x)(y))) )


Ydelay F =  (lambda f : (lambda x : f(x(x))) (lambda x : f(x(x)))) F
         -> (lambda x : F(x(x))) (lambda x : F(x(x)))
         -> F( (lambda x : F(x(x))) (lambda x : F(x(x))) )

延迟发生在哪里?在这两种情况下F都是顶级表达式,并且在这两种情况下lambda x都在 level below F。延迟起什么作用lambda y

编辑:

同样,为什么延迟在这里的第一行是如何工作的:

(lambda x : x(x)) (lambda y: lambda x : x(x)(y))
(lambda x : x(x)) (lambda x: x(x))
4

1 回答 1

1

当我们将 lambda 表达式转换为普通函数语法时,整个事情变得更加明显:

def f(x):                       # lambda x : x(x)
    return x(x)

def g(y):                       # lambda y: lambda x : x(x)(y)
    def fg(x):
        return (x(x))(y)
    return fg


f(g) # does not recurse infinitely

当我们手动评估对应的表达式时,(lambda x : x(x)) (lambda y: (lambda x : x(x))(y))我们得到

f(g) = g(g) = lambda x : x(x)(g)

同时评估与(lambda x : x(x)) (lambda y: y(y))产量相对应的那个

f(f) = f(f) = f(f) = ...

现在我们可以看到为什么抽象会停止递归。

于 2018-03-04T12:04:19.437 回答