学习笔记:λ演算(lambda calculate)

索引链接至此博客 负暄琐话

这几篇是讲lambda演算的,博主翻译了国外文章,在此也向译者的劳动表示致敬!

找个时间静下心来,配合着《the root of lisp》以及《实用common lisp编程》学习下这种计算模型

good math, bad math之Lambda算子简介

lambda算子1

lambda算子2

lambda算子3 算子里的数

lambda算子4 布尔值和选择

lambda算子5a Y组合子

lambda算子5b 还是Y组合子

另外还有《the root of lisp》的 Chinese Translation

 

笔记一:Lambda的三种表达式(对应索引:简介、算子1)------------------2012.12.24 01:01

1.函数定义

语法: lambda x."函数体"

表示定义了一个带参数x的函数,将x赋值后返回字符串"函数体"所代表的值,之所以要用字符串这个概念,是因为lambda calculate不仅仅能定义出代 数运算,它能表示的是更多的演算,所以需要从更抽象的层面来理解它

举个例子,对于下列表达式:

1.x + y 

2.if(x == true) then do f(y)

3.p(x)-->q(y)

4.lambda y.(x+y)

有代数式,编程语言,逻辑命题,甚至还有lambda函数本身。但是对于lambda calculate来说都属于它能处理的范畴,都可以被视为一个字符串,只要对该字符串声明了参数, 使用函数时为该参数赋值,就能得到该字符串所代表的表达式或值,表达式的意义及其本身的值由人类意识来决定。

2.标识符引用(identifier reference)

“一个标识符引用就是一个名字。这个名字和包括这个引用的函数定义里的参数同名。”

原博客译者后来又说到,标识符其实就是变量。按我的理解,所谓的引用就是在进行 lambda calculate时写下某个变量的名字。

3.函数引用

就是为函数的参数赋值,然后这个函数就变成一个确定的表达式,语法: (lambda x."函数体")value

例:已知有函数lambda x.x+3 当通过函数引用为参数赋值时,这个函数就会返回一个 有现实意义的字符串。

    对于这个函数的参数x,这里我们给它赋值为5,即(lambda  x.x+3)5,那么通过这种演算过后,表达式"lambda x.x+3"就要被改写成新的表达式"5+3"

        简而言之,若有F(x) == "lambda x.x+3",当执行完x="5"时,可得F(5) == "5+3"

另外顺便先提下,lambda函数本身也可以作为变量值,假设我们干了这么一件 事:x="lambda y.y*5",那么可得F(lambda y.y*5)=="lambda y.y*5+3",这个字符串刚好是一个新的lambda函数。

我一直用双引号括起一些东西,只是想提醒下读者要把不同领域的表达式等同地视为字符串,lambda calculate处理的就是这些具有数理逻辑意义的字符串。个人觉得,想要理解它,需要有比学微积分时更抽象的抽象。越抽象的东西,越能描述出不同事物的统一性。

笔记二:几个概念(对应索引:算子2)------------------2012.12.25 13:50

有界变量与自由变量

若表达式中存在这么一个变量,它的名字与该表达式的参数声明中所声明的参数名一样,则该变量称为该表达式的有界变量,否则称为自由变量。

例1lambda x . plus x y 该表达式声明了一个参数x,所以函数体"plus x y"x对于该表达式是一个有界变量,而变量plusy则是自由变量。

2lambda y . (lambda x . plus x y) 变量y对于内嵌表达式lambda x . plus x y是一个自由变量,但对于整个表达式来说,它是一个有界变量

 

多参函数

定义这么一个函数f(x,y) = "plus x y"

到了lambda calculate中则应该这样写 lambda y.(lambda x.plus x y)

括弧内函数接收一个参数x,返回的是一个表达式"plus x y"

当括弧内参数x被传递了实参之后,整个表达式就变成 lambda y.plus x y

括弧外的函数接收一个参数y

当它的参数y也被赋值以实参后,整个函数就返回表达式"plus x y"

这样,通过函数嵌套使得参数xy都分别有一个函数进行处理,再加上传递实参后返回的表达式是"plus x y"

所以f(x,y) = "plus x y"lambda y.(lambda x.plus x y)等价

后面译者又提到,为了方便表达

类似lambda y.(lambda x.plus x y)的多参数函数会被写成lambda x y.plus x y

 

Alpha转换

给已声明的参数改名,改名后与该参数对应的表达式内的有界变量也要跟着改名。这个与上面的“有界变量与自由变量”记得在离散数学的数理逻辑中也有提到

 

Beta简化

其实就是前一笔记中提到的函数引用,就是一种在进行lambda演算时对表达式(或着说字符串)进行改写所依据的规则,还是用译者翻译过来的三个例子吧,嚼下别人吐出的甘蔗渣。

1.引用这么一个函数"lambda x . x + 1",我们给它的参数x赋值实参3,那么表达式的改写过程是这样"lambda x . x + 1"-------->"(lambda x . x + 1)3" -------> "3+1"

2. lambda y . (lambda x . x + y)q  ------> lambda x, x+q 这个函数有一点特殊的意义。通过函数引用,从一个表达式产生了新的表达式,这个新的表达式会根据所传实参的不同而不同。到了编程语言里,这种特性的意义就是使得该语言能够将其自身代码作为它能处理的数据对象。也就是说,你能够用代码产生新代码!恩,我联想到了common lisp里面的宏,联想到了《the root of lisp》中仅用七个原始运算符就编写出了lisp的“编译器”的神奇一幕,这七个运算符构成了一个计算模型,实现了lisp语言本身,而这些运算符又属于lisp语言

3.好,最后一堆甘蔗渣 ^_^||| 

让我们来用Beta规则来简化这个"(lambda x y. x y) (lambda z . z * z) 3",我只给出过程哈

先还原为多参数函数的lambda calculate标准写法:((lambda x.(lambda y.xy))(lambda z.z*z))3

第一步。。。:"(lambda y.(lambda z.z*z)y)3" 

第二步"(lambda z.z*z)3"

最后一步!"3*3" 

搞定!继续复习期末考试去 O(_)O哈哈~

笔记三:毁了我有关数的世界观的“丘其数”(对应索引:算子3)------------------2013.01.19 18:14

 

Mark原文一段严格定义,我想通过这段mark就能说明看起来很简单的Alpha转化必须存在的原因:

我们用"free(x)"来代表表达式x里所有自由变量的集合。

lambda x . B e = B[x := e] if free(e) /subset free(B[x := e])

这个定义末尾的条件,"if free(e) /subset free(B[x:=e])"道出了我们需要Alpha转换的原因:仅当beta化简不会引起有界变量和自由变量的冲突时,我们可以实施Beta化简。如果一个变量“z”是"e"里的自由变量,那我们得保证beta化简不会让"z"变成有界变量。如果B里的有界变量和”e"里的自由变量重名,我们必须先用Alpha转换,使得重名的变量不再重名。形式化定义不够直观,直观描述又不够简洁。

Mark END

小标题不夸张,这部分真的有点毁世界观,有点像抽象代数。

lambda演算中,我们曾经认识的数其本质被赋予了新的意义——函数。数也是一种函数,称为丘其数,能被传递实参,返回函数值。自然数所对应的丘其数的定义如下

0: lambda s z.z

1: lambda s z.s z

2: lambda s z.s (s z)

3: lambda s z.s(s(s z))

......

n: lambda s z.s(s(....(s(s(s(s z)))....)) 这里有n个s,n-1对括弧

定义一个函数add,即加法运算:lambda xy.(lambda sz.(x s(y s z)))

我们用它来计算2+3。即为x传递实参lambda s2 z2.s2(s2 z2),为y传递实参 lambda s3 z3.s3(s3(s3 z3))

那么2+3 ==

1) lambda x y.lambda s z.x s(y s z)   lambda s2 z2.s2(s2 z2) lambda s3 z3.s3(s3(s3 z3))

2) lambda s z.(lambda s2 z2.s2(s2 z2) s (lambda s3 z3.s3(s3(s3 z3)) s z))

3) lambda s z.lambda s2 z2.s2(s2 z2) s (s(s(s z)))
到了这一步,对于函数lambda s2 z2.s2(s2 z2)我们有两件事要做
1.将s作为s2的实参
2.将(s(s(s z)))作为z2的实参,然后能得以下函数
4) lambda s z.s(s(s(s(s z)))) 数一数,函数体中一共有5个s,根据丘其数的定义,该函数正好对应自然数5
总之记住一句话:在lambda中,数也同样是函数,而貌似有个人定义了很多lambda函数(例如上面那个add函数),将其作为"丘其数"函数的参数,从而在lambda演算中等价地实现了我们熟悉的四则运算,当然肯定还有别的运算被人用lambda演算给重新定义了一遍。
笔记四:。。。(对应索引:算子4、算子5a)------------------2013.01.21 23:40
 
笔记五:递归?Y组合子的应用?(对应索引:算子5a)------------------2013.01.23 00:13
由于lambda演算中采用的是匿名函数,要写递归函数就得通过特殊途径
例如我们想写这样一个东西:
let fact = lambda n . IsZero n 1 (Mult n (fact (Pred n))) 也就是说我们将这个函数命名为fact且试图在fact中递归调用fact
这样的话会出现问题:fact并没有被定义,无法被引用,所以要通过Y组合子来实现递归的匿名函数
Y组合子定义如下:lambda y . (lambda x . y (x x)) (lambda x . y (x x))
有重要特性:Y F = F(Y F),这样就能不断展开产生递归效果:F(F(F(F......(F(Y F))......)))
把上面那个不合法的fact改写下:
lambda fact.(lambda n . IsZero n 1 (Mult n (fact (Pred n)))) 并将此函数简写为newFact:
letnewFact = lambda fact.(lambda n . IsZero n 1 (Mult n (fact (Pred n))))
然后所谓的递归函数是这样来的:Y newFact
不知道这样理解对不对,其实递归函数并不是一个被定义的函数,而是一个Y组合子的引用过程
好吧,先这样吧,不是特别懂,但至少先把看似懂了的东西记下来再说。

posted on 2012-12-22 20:46  denallo  阅读(2023)  评论(0编辑  收藏  举报

导航