`
Joson_Coney
  • 浏览: 54638 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

【转】λ演算

 
阅读更多

(转自wiki:http://zh.wikipedia.org/zh-cn/%CE%9B%E6%BC%94%E7%AE%97

 

λ演算

维基百科,自由的百科全书
跳转到: 导航 , 搜索

λ演算lambda calculus )是一套用于研究函数 定义、函数应用和递归形式系统 。它由阿隆佐·邱奇 和他的学生斯蒂芬·科尔·克莱尼20世纪 30年代 引入。邱奇运用λ演算在1936年 给出判定性问题 (Entscheidungsproblem)的一个否定的答案。这种演算可以用来清晰地定义什么是一个可计算函数 。关于两个lambda演算表达式是否等价的命题无法通过一个“通用的算法”来解决,这是不可判定性能够证明的头一个问题,甚至还在停机问题 之先。Lambda演算对函数式编程语言 有巨大的影响,比如Lisp语言ML语言Haskell语言

Lambda演算可以被称为最小的通用程序设计语言。它包括一条变换规则(变量替换)和一条函数定义方式,Lambda演算之通用在于,任何一个可计算函数都能用这种形式来表达和求值。因而,它是等价于图灵机 的。尽管如此,Lambda演算强调的是变换规则的运用,而非实现它们的具体机器。可以认为这是一种更接近软件而非硬件的方式。

本文讨论的是邱奇的“无类型lambda演算”,此后,已经研究出来了一些有类型lambda演算

目录

 [隐藏

[编辑 ] 历史

最开始,邱奇试图创制一套完整的形式系统作为数学的基础,当他发现这个系统易受罗素悖论 的影响时,就把lambda演算单独分离出来,用于研究可计算性 ,最终导致了他对判定性问题 的否定回答。

[编辑 ] 非形式化的描述

在lambda演算中,每个表达式都代表一个只有单独参数的函数,这个函数的参数本身也是一个只有单一参数的函数,同时,函数的值是又一个只有单一 参数的函数。函数是通过lambda表达式匿名地定义的,这个表达式说明了此函数将对其参数进行什么操作。例如,“加2”函数f(x)= x + 2可以用lambda演算表示为λx.x + 2 (或者λy.y + 2,参数的取名无关紧要)而f(3)的值可以写作(λx.x + 2) 3。函数的应用(application)是左结合 的:f x y =(f x) y。考虑这么一个函数:它把一个函数作为参数,这个函数将被作用在3上:λf.f 3。如果把这个(用函数作参数的)函数作用于我们先前的“加2”函数上:(λf.f 3)(λx.x+2),则明显地,下述三个表达式:

(λf.f 3)(λx.x+2)与 (λx.x + 2) 3与3 + 2

是等价的。有两个参数的函数可以通过lambda演算这么表达:一个单一参数的函数的返回值又是一个单一参数函数的参数(参见Currying )。例如,函数f(x, y) = x - y可以写作λx.λy.x - y。下述三个表达式:

(λx.λy.x - y) 7 2与 (λy.7 - y) 2与7 - 2

也是等价的。然而这种lambda表达式之间的等价性无法找到一个通用的函数来判定。

并非所有的lambda表达式都可以规约至上述那样的确定值,考虑

(λx.x x)(λx.x x)

(λx.x x x)(λx.x x x)

然后试图把第一个函数作用在它的参数上。 (λx.x x)被称为ω 组合子 ,((λx.x x)(λx.x x))被称为Ω,而((λx.x x x) (λx.x x x))被称为Ω2 ,以此类推。

若仅形式化函数作用的概念而不允许lambda表达式,就得到了组合子逻辑

[编辑 ] 形式化定义

形式化地,我们从一个标识符(identifier)的可数无穷 集合 开始,比如{a, b, c, ..., x, y, z, x1 , x2 , ...},则所有的lambda表达式可以通过下述以BNF 范式表达的上下文无关文法 描述:

  1. <expr> ::= <identifier>
  2. <expr> ::=(λ<identifier> .<expr>)
  3. <expr> ::=(<expr> <expr>)

头两条规则用来生成函数,而第三条描述了函数是如何作用在参数上的。通常,lambda抽象(规则2)和函数作用(规则3)中的括号在不会产生歧义 的情况下可以省略。如下假定保证了不会产生歧义:(1)函数的作用是左结合的,和(2)lambda操作符被绑定到它后面的整个表达式。例如,表达式 (λx.x x)(λy.y) 可以简写成λ(x.x x) λy.y 。

类似λx.(x y)这样的lambda表达式并未定义一个函数,因为变量y的出现是自由的 ,即它并没有被绑定到表达式中的任何一个λ上。变量出现次数的绑定是通过下述规则(基于lambda表达式的结构归纳 地)定义的:

  1. 在表达式V中,V是变量,则这个表达式里变量V只有一次自由出现。
  2. 在表达式λV .E中(V是变量,E是另一个表达式),变量自由出现的次数是E中变量自由出现的次数,减去E中V自由出现的次数。因而,E中那些V被称为绑定在λ上。
  3. 在表达式 (E E')中,变量自由出现的次数是E和E'中变量自由出现次数之和。

在lambda表达式的集合上定义了一个等价关系 (在此用==标注),“两个表达式其实表示的是同一个函数”这样的直觉性判断即由此表述,这种等价关系是通过所谓的“alpha-变换规则”和“beta-归约规则”。

[编辑 ] 归约

[编辑 ] α-变换

Alpha-变换规则表达的是,被绑定变量的名称是不重要的。比如说λx.x和λy.y是同一个函数。尽管如此,这条规则并非像它看起来这么简单,关于被绑定的变量能否由另一个替换有一系列的限制。

Alpha-变换规则陈述的是,若V与W均为变量,E是一个lambda表达式,同时E[V:=W]是指把表达式E中的所有的V的自由出现都替换为W,那么在W不是 E中的一个自由出现,且如果W替换了V,W不会被E中的λ绑定的情况下,有

λV.E == λW.E[V:=W]

这条规则告诉我们,例如λx.(λx.x) x这样的表达式和λy.(λx.x) y是一样的。

[编辑 ] β-归约

Beta-归约规则表达的是函数作用的概念。它陈述了若所有的E'的自由出现在E [V:=E']中仍然是自由的情况下,有

((λV.E) E') == E [V:=E']

成立。

==关系被定义为满足上述两条规则的最小等价关系(即在这个等价关系中减去任何一个映射,它将不再是一个等价关系)。

对上述等价关系的一个更具操作性的定义可以这样获得:只允许从左至右来应用规则。不允许任何beta归约的lambda表达式被称为Beta范式 。 并非所有的lambda表达式都存在与之等价的范式,若存在,则对于相同的形式参数命名而言是唯一的。此外,有一个算法用户计算范式,不断地把最左边的形 式参数替换为实际参数,直到无法再作任何可能的规约为止。这个算法当且仅当lambda表达式存在一个范式时才会停止。Church-Rosser定理 说明了,当且仅当两个表达式等价时,它们会在形式参数换名后得到同一个范式。

[编辑 ] η-变换

前两条规则之后,还可以加入第三条规则,eta-变换,来形成一个新的等价关系。Eta-变换表达的是外延性 的概念,在这里外延性指的是,两个函数对于所有的参数得到的结果都一致,当且仅当它们是同一个函数。Eta-变换可以令λx .f x和f相互转换,只要x不是f中的自由出现。下面说明了为何这条规则和外延性是等价的:

若f与g外延地等价,即,f a == g a对所有的lambda表达式a成立,则当取a为在f中不是自由出现的变量x时,我们有f x == g x,因此λx .f x == λx .g x,由eta-变换f == g。所以只要eta-变换是有效的,会得到外延性也是有效的。

相反地,若外延性是有效的,则由beta-归约,对所有的y有(λx .f x) y == f y,可得λx .f x == f,即eta-变换也是有效的。

[编辑 ] lambda演算中的算术

在lambda演算中有许多方式都可以定义自然数 ,但最常见的还是邱奇数 ,下面是它们的定义:

0 = λf.λx.x
1 = λf.λx.f x
2 = λf.λx.f (f x)
3 = λf.λx.f (f (f x))以此类推。直观地说,lambda演算中的数字n就是一个把函数f作为参数并以f的n次幂为返回值的函数。换句话说,邱奇整数是一个高阶函数 -- 以单一参数函数f为参数,返回另一个单一参数的函数。

(注意在邱奇原来的lambda演算中,lambda表达式的形式参数在函数体中至少出现一次,这使得我们无法像上面那样定义0)在邱奇整数定义的基础上,我们可以定义一个后继函数,它以n为参数,返回n + 1:

SUCC = λn.λf.λx.f(n f x)

加法是这样定义的:

PLUS = λm.λn.λf.λx.m f (n f x)

PLUS可以被看作以两个自然数为参数的函数,它返回的也是一个自然数。你可以试试验证

PLUS 2 3 与 5

是否等价。乘法可以这样定义:

MULT = λm.λn.m(PLUS n)0,

即m乘以n等于在零的基础上n次加m。另一种方式是

MULT = λm.λn.λf.m(n f)

正整数n的前驱元(predecessesor)PRED n = n - 1要复杂一些:

PRED = λn.λf.λx.n(λg.λh.h(g f))(λu.x)(λu.u)

或者

PRED = λn.n(λg.λk.(g 1)(λu.PLUS(g k) 1) k)(λl.0) 0

注意(g 1)(λu.PLUS(g k) 1) k表示的是,当g(1)是零时,表达式的值是k,否则是g(k)+ 1。

[编辑 ] 逻辑与谓词

习惯上,下述两个定义(称为邱奇布尔值)被用作TRUEFALSE 这样的布尔值:

TRUE := λx y.x
FALSE := λx y.y
(注意FALSE 等价于前面定义邱奇数零)

接着,通过这两个λ-项,我们可以定义一些逻辑运算:

AND := λp q.p q FALSE
OR := λp q.p TRUE q
NOT := λp.p FALSE TRUE
IFTHENELSE := λp x y.p x y

我们现在可以计算一些逻辑函数,比如:

AND TRUE FALSE
≡(λp q.p q FALSE) TRUE FALSE →β TRUE FALSE FALSE
≡(λx y.x) FALSE FALSE →β FALSE

我们见到AND TRUE FALSE 等价于FALSE

“谓词”是指返回布尔值的函数。最基本的一个谓词是ISZERO ,当且仅当其参数为零时返回真,否则返回假:

ISZERO := λn.n(λx.FALSE) TRUE

运用谓词与上述TRUEFALSE 的定义,使得"if-then-else"这类语句很容易用lambda演算写出。

[编辑 ] 有序对

有序对(2-元组)数据类型可以用TRUEFALSEIF 来定义。

CONS := λx y.λp.IF p x y
CAR := λx.x TRUE
CDR := λx.x FALSE

链表数据类型可以定义为,要么是为空列表保留的值(e.g.FALSE ),要么是CONS 一个元素和一个更小的列表。

[编辑 ] 递归

递归 是使用函数自身的函数定义;在表面上,lambda演算不允许这样。但是这种印象是误解。考虑个例子,阶乘 函数f(n) 递归的定义为

f(n):= if n = 0 then 1 else n·f(n-1)

在lambda演算中,你不能定义包含自身的函数。要避免这样,你可以开始于定义一个函数,这里叫g ,它接受一个函数f 作为参数并返回接受n 作为参数的另一个函数:

g := λf n.(if n = 0 then 1 else n·f(n-1))

函数g 返回要么常量1 ,要么函数fn-1 的n次应用。使用ISZERO 谓词,和上面描述的布尔和代数定义,函数g 可以用lambda演算来定义。

但是,g 自身仍然不是递归的;为了使用g 来建立递归函数,作为参数传递给gf 函数必须有特殊的性质。也就是说,作为参数传递的f 函数必须展开为调用带有一个参数的函数g -- 并且这个参数必须再次f 函数!

换句话说,f 必须展开为g(f) 。这个到g 的调用将接着展开为上面的阶乘函数并计算下至另一层递归。在这个展开中函数f 将再次出现,并将被再次展开为g(f) 并继续递归。这种函数,这里的f = g(f) ,叫做g 的不动点,并且它可以在lambda演算中使用叫做悖论算子不动点算子 来实现,它被表示为Y -- Y组合子

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

在lambda演算中,Y gg 的不动点,因为它展开为g(Y g) 。现在,要完成我们对阶乘函数的递归调用,我们可以简单的调用 g(Y g)n ,这里的n是我们要计算它的阶乘的数。

比如假定n = 5,它展开为:

(λn.(if n = 0 then 1 else n·((Y g)(n-1)))) 5
if 5 = 0 then 1 else 5·(g(Y g,5-1))
5·(g(Y g)4)
5·(λn.(if n = 0 then 1 else n·((Y g)(n-1))) 4)
5·(if 4 = 0 then 1 else 4·(g(Y g,4-1)))
5·(4·(g(Y g)3))
5·(4·(λn.(if n = 0 then 1 else n·((Y g)(n-1))) 3))
5·(4·(if 3 = 0 then 1 else 3·(g(Y g,3-1))))
5·(4·(3·(g(Y g)2)))
...

等等,递归的求值算法的结构。所有递归定义的函数都可以看作某个其他适当的函数的不动点,因此,使用Y 所有递归定义的函数都可以表达为lambda表达式。特别是,我们现在可以明晰的递归定义自然数的减法、乘法和比较谓词。

[编辑 ] 可计算函数和lambda演算

自然数 的函数F: NN 可计算函数当且仅当 存在着一个lambda表达式f ,使得对于N 中的每对x, y都有F( x) = y当且仅当f x == y ,这里的xy 分别是对应于x和y的邱奇数 。这是定义可计算性的多种方式之一;关于其他方式很它们的等价者的讨论请参见邱奇-图灵论题

[编辑 ] 参见

[编辑 ] 外部链接

分享到:
评论

相关推荐

    λ-演算的语法和语义

    λ-演算的语法和语义

    λ演算的语法和语义.zip

    一本关于丘奇演算的教材,电子书,作者是荷兰学者H.P.巴伦德莱赫特

    λ演算解释器

    λ演算解释器 如所述的lambda演算的解释器 语法 : &lt;λexp&gt;::= |(λ &lt;λexp&gt;) |(&lt;λexp&gt; &lt;λexp&gt;) 例子: &gt;&gt; (lambda x x) λx. x &gt;&gt; (lambda x y) λx. y &gt;&gt; x x &gt;&gt; 5 5 &gt;&gt; ((lambda x 5)...

    lambda演算和图灵机

    λ演算(lambda calculus)是一套用于研究函数定义、函数应用和递归的形式系统。它由阿隆佐·邱奇和他的学生斯蒂芬·科尔·克莱尼在20世纪30年代发明的。 λ演算可以被称为最小的通用程序设计语言。它包括一条变换...

    mikrokosmos:(λ)教育性λ演算解释器

    mikrokosmos:(λ)教育性λ演算解释器

    λ演算

    λ演算

    lambda演算学习

    λ演算的学习资料,英文版,可计算函数和lambda演算规则

    stlc:简单类型λ演算的优化类型推断

    这是用于简单类型(或“单态”)λ演算的优化类型推断算法。 编写该算法的简单实现非常简单,但是在Haskell之前,我对C中“文字”方法的效率低下感到不满,为每个术语和每种类型分配一个结构,等等。所以我尽我所能...

    λ演算的乐趣!-JavaScript开发

    λ在JS探索中使用Lambda演算:$节点lambda.js摆弄它(首先安装npm):$ npm测试注意:这些都是函数,因此请期待您的堆栈。λ在JS探索中使用Lambda演算。 lambda.js摆弄它(首先安装npm):$ npm test注意:这些是...

    lunarflow:λ演算

    初学者使用直观的可视化工具学习lambda演算的工具。 这仍处于超级早期,所以也许启动此仓库并在几个月后回来:D 尝试。 该项目尚处于初期阶段,因此链接可能会导致空白页。设置克隆仓库跑步: pnpm install开发...

    reduck:无类型λ演算的交互式工具

    一种可以在 lambda 演算中逐步找到项的(β-nf) 的工具。 它基本上是一种抽象函数式编程语言的解释器,它显示了评估过程中的每个计算步骤。 评估策略是call by name ,即它执行函数中的参数的替换(β-规则)而不评估...

    lambdapi:基于λΠ演算模重写的证明助手

    Lambdapi,基于λΠ-演算模数重写的证明助手 Lambdapi是基于λΠ演算模数重写的证明助手。它带有Emacs和VSCode扩展。中提供了更多详细信息。 Lambdapi文件必须以.lp 。但是Lambdapi也可以读取文件(扩展名.dk )并将...

    Introduction to Lambda Calculus

    λ演算,λ(Lambda(大写Λ,小写λ)读音:lan b(m) da(兰亩达)['læ;mdə])演算是一套用于研究函数定义、函数应用和递归的形式系统。它由 Alonzo Church 和 Stephen Cole Kleene 在 20 世纪三十年代引入,...

    magic-in-ten-mins-cpp

    中等 | 简单类型 λ 演算(Simply-Typed Lambda Calculus)[C++面向对象基础,ADT,λ 演算] 中等 | 系统 F(System F)[C++面向对象基础,ADT,简单类型 λ 演算] 较难 | 构造演算(Calculus of Construction)[C++面向...

    高级数理逻辑第七章

    高级数理逻辑第七章:λ-演算(Lambda 演算);λ-演算是一套用于研究函数定义、函数应用和递归的形式系统

    lambda演算入门

    函数式编程入门概念之一,lambda演算是函数式编程的数学理论基础...

    glc:球拍中的Lambda演算

    λ演算自述文字在这里。

    hope编程语言

    Hope是一个早期的函数式编程语言,有助于学习函数式编程和λ演算 Hope is a small functional programming language developed in the 1970s at the University of Edinburgh. It predates Miranda and Haskell and ...

    Dedukti:λΠ演算模重写的实现

    为了进行互操作性开发,仍使用Dedukti的当前版本。DEDUKTI用户手册(开发版本)安装要编译(和可选地安装) Dedukti您需要: OCaml &gt;= 4.06 , Menhir dune odoc (仅适用odoc doc)。安装OPAMopam install dedukti...

    learn you a haskell

    Haskell语言是1990年在编程语言Miranda的基础上标准化的,并且以λ演算为基础发展而来。这也是为什么Haskell语言以希腊字母“λ”(Lambda)作为自己的标志。Haskell语言的最重要的两个应用是Glasgow Haskell ...

Global site tag (gtag.js) - Google Analytics