用Lamda演算表达递归

0.用Lambda方式描述递归

在很多编程语言中,都可以用Lamda表达式来定义函数,这种函数最大的特点就是没有名字。

函数的Lamda表示法:

λ<name>.<expression>

虽然在大部分关于Lamda calculus 的文献中,函数是被定义成这个样子的,但是λ符号后面的那个name, 并不代表函数的名字,而是指这个函数的参数, 所以Lambda表达式是匿名的函数。在大部分编程语言中,都是通过函数的名字来对函数进行调用的。Lamda本身就是没有名字的,没有名字要如何调用函数呢?

在普通的递归函数中,通过名字来调用函数自身;但在λ演算中,把整个函数的λ表达式写在参数的前面,即表示对函数的调用。如果用和普通非匿名函数同样的方法写匿名的递归函数,当这个函数需要调用自身的时候,就要把自身的表达式完整的写一遍,然而在表达式内又有对自身的调用;怎么办?再写一遍,这样写下去是无穷无尽的。 所以在Lamda函数中调用自身根本就是被禁止的。

这就是这篇东西要解释的问题,到底要如何用λ表达式来写递归函数?

1.不动点

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

 

2.不动点组合子Fixed-point combinator,或不动点算子

对于一阶函数来说,不动点是一个一阶值,高阶函数f的不动点是另一个函数g使得

f(g) = g

那么,不动点算子是任何函数fix使得对于任何函数f都有

f(fix(f)) = fix(f)

这东西有什么用处呢? 把它反过来看

fix(f)  = f(fix(f))

进而:

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

很好,看上去像递归了。是的,不动点组合子允许定义匿名的递归函数。它使得用非递归的Lamda方式定义递归函数成为可能。

 

3.Y组合子(Y-Combinator)

(可能是最简单的)不动点组合子叫做Y组合子。它是Haskell.B.Curry发现的,定义为

Y   :=  λf.(λx.(f (x x)) λx.(f (x x)))                 把它应用在另一函数g上

 

1. Y g  =   λf.(λx.(f (x x)) λx.(f (x x)))  g 

2.      = λx.(g (x x)) λx.(g (x x)))                     对line1, 用g对λf进行β归约得到

3.      = g (λx.(g (x x))(λx.(g (x x))                  对line2, 用 λx.(g (x x))对开头的λx进行β归约得到      

4.      = g (λf.(λx.(f (x x)) λx.(f (x x)))  g)        注意看第3行g后面的部分,和第2行完全一样,第2行和第1行等号右边又是一样的

5.      = g (Y g)

无类型Lamda中还存在其他的组合子。

Haskell 语言和柯里化都是从Haskell.B.Curry 的名字由来的。

     

 4.用Haskell的Lambda表达式实现1+2+...+n

未完待续...

posted on 2015-12-24 17:27  Gangster  阅读(598)  评论(0编辑  收藏  举报