Y组合子是用于lambda演算中实现递归逻辑的。

当我们用erlang实现一个递归的匿名函数来完成阶乘,第一印象应该会写成如下这种形式:

F = fun(1) -> 1;

     (N) -> N * F(N -1) end.

如果我们在程序代码中按照这种方式书写,在数学推演中是不可能的,(虽然现代编程语言中有些编译器可以识别的),因为在lambda函数定义时,F还根本没有意义。

这时候我们该怎么办呢, 如果要实现这种递归函数, 我能想到的就是把这个F变成约束变量,那么可以假设出另外存在一个高阶函数,将F作为参数传递进去,则得到

F = fun(Func) ->

    fun(1) -> 1;

        (N) -> N * Func(N - 1) end

    end.

然后形如F(Func)(x)这种方式调用便可以说的通了,这样F(Func)便是我们所实现的递归函数了。但这里有个问题,传递给F的Func函数又是什么呢?

从逻辑上说,Func的定义只能如下:

Func = fun(1) -> 1;


        (N) -> N * Func(N - 1) end.

尼玛,这不又变成了一开始的问题嘛,Func的定义里怎么又能调用Func呢,那我们只能继续通过这种方式来变换了Func = F(Func). 函数调用则变成了F(F(Func))(x),可是Func还是无解啊,难道继续变化下去吗?

好吧,难道到是鸡生蛋,蛋生鸡的问题嘛……撞墙= =

 

现在问题的关键就在于消去Func了,那我们接着引入不动点的概念 :

函数 f 的不动点是一个值 x 使得 f(x) = x。例如,0 和 1 是函数 f(x) = x2 的不动点,因为 02 = 0 而 12 = 1。

鉴于一阶函数(在简单值比如整数上的函数)的不动点是个一阶值,高阶函数 f 的不动点是另一个函数 g 使得 f(g) = g

那么,不动点组合子(Fixed-point combinator) 是任何函数 fix 使得对于任何函数 f 都有f(fix(f)) = fix(f).

使得F(Fix(F)) = Fix(F),这样也看不出什么东西来,但如果把Fix(F) 看成Func的话就变成了 F(Func) = Func,这样就可以避免上面那种无限的调用变换的情况了。我们需要的就是求出这个Fixed-point combinator。

 

刚才我们在上面说到,我们假设Func是一个调用了自身的完美的递归函数,所以,我们要再来解决一个问题,如何在lambda演算中凭空变出递归的形式,

刚才我们在上面说的,把函数当参数传递进去,然后再调用,这样就可以避免匿名函数不能调用自身的问题。

这样通过这种方式,我们可以添加一个中间层来用一个高阶函数来把F(Func)表达出来:

FuncGen = λ Self . F(Self(Self))  (注意,在现在的这种写法中,F为自由变量) ,这时候FuncGen(FuncGen)展开则可以变换为F(FuncGen(FuncGen)),

所以可以得出 F(Func) = FuncGen(FuncGen) = F(FuncGen(FuncGen)),大家再对比一下上面提到的不动点F(Fix(F)) = Fix(F),是不是觉得很相似了= =

但这时候我们还要解决一个问题,刚刚FuncGen中的F是自由变量,我们需要把他变为约束变量,那我们再添加一个中间层,假设为函数Y(F),结合起来,这便是大名鼎鼎的Y组合子了

Y = fun(FFunc) ->
      FGen = fun(FGen) -> FFunc(fun(N) -> (FGen(FGen))(N)) end,
        FGen(FGen) end,

又因为F(Y(F)) = Y(F),所以函数(F(Y(F)))(x) 和(Y(F))(x)又是等价的了。

(F(Y(F)))(x)就是真正的递归阶乘函数了。

写累死……有些地方的确不好理解,数学不好的孩纸伤不起啊= =

 

 

 posted on 2013-01-05 18:09  文武双全大星星  阅读(692)  评论(0编辑  收藏  举报