简单λ演算

  苦于Lambda演算的资料又少不完,上个星期在网上收集了资料以后汇集在一起,作为一个全一点的版本吧!基本上是结合这篇文章,Wiki中的介绍,再就是<<类型和程序设计语言>>,这三者写的.希望大家对其中的错误提出指正,还希望和大家讨论讨论.

简单λ演算

Lambda演算简介:

Wikipedia(维基百科全书)中关于lambda演算的解释如下:

The lambda calculus is a formal system designed to investigate function definition, function application, and recursion. It was introduced by Alonzo Church and Stephen Cole Kleene in the 1930s; Church used the lambda calculus in 1936 to give a negative answer to the Entscheidungsproblem. The calculus can be used to cleanly define what a computable function is. The question of whether two lambda calculus expressions are equivalent cannot be solved by a general algorithm, and this was the first question, even before the halting problem, for which undecidability could be proved. Lambda calculus has greatly influenced functional programming languages, especially Lisp.

λ演算是一套用于研究函数定义、函数应用和递归形式系统。它由 Alonzo ChurchStephen Cole Kleene 在 20 世纪三十年代引入,Church 运用 lambda 演算在 1936 年给出 判定性问题 (Entscheidungsproblem) 的一个否定的答案。这种演算可以用来清晰地定义什么是一个可计算函数。关于两个 lambda 演算表达式是否等价的命题无法通过一个通用的算法来解决,这是不可判定性能够证明的头一个问题,甚至还在停机问题之先。Lambda 演算对函数式编程有巨大的影响,特别是Lisp 语言

同时,数理逻辑中对于lambda演算的介绍就简单得多:

λ-演算可以说是最简单、最小的一个形式系统。它是在二十世纪三十年代由Alonzo ChurchStephen Cole Kleene发明的。至今,在欧洲得到了广泛的发展。可以说,欧洲的计算机科学是从λ-演算开始的,而现在仍然是欧洲计算机科学的基础,首先它是函数式程序理论的基础,而后,在λ-演算的基础上,发展起来的π-演算、χ-演算,成为近年来的并发程序的理论工具之一,许多经典的并发程序模型就是以π-演算为框架的。

Lambda 演算可以被称为最小的通用程序设计语言。它包括一条变换规则 (变量替换) 和一条函数定义方式,Lambda 演算之通用在于,任何一个可计算函数都能用这种形式来表达和求值。因而,它是等价于图灵机的。尽管如此,Lambda 演算强调的是变换规则的运用,而非实现它们的具体机器。可以认为这是一种更接近软件而非硬件的方式。 Lambda演算表达了两个计算机计算中最基本的概念“代入”和“置换”。“代入”我们一般理解为函数调用,或者是用实参代替函数中的形参;“置换”我们一般理解为变量换名规则。后面会讲到,“代入”就是用lambda演算中的β-归约概念。而“替换”就是lambda演算中的α-变换。

目录

  • 1 历史
  • 2 非形式化的描述
  • 3 形式化的定义
    • 3.1字母表
    • 3.2 λ-项
    • 3.3 公式
    • 3.4 λ-项中的变量自由出现法则
    • 3.5 λ x的控制域
  • 4 变换与等价关系
    • 4.1 α-变换
    • 4.2 β-归约
    • 4.3 等价关系
    • 4.4 η-变换
  • 5 λ演算的操作语义
  • 6 λ演算的公理化和定理
    • 6.1 λ演算的公理
    • 6.2 不动点理论
    • 6.3 Church-Rosser定理及其等价定理
  • 7 lambda 演算中的程序设计
    • 7.1 自然数与运算
    • 7.2 逻辑与断言
    • 7.3 递归
  • 8 参考文献和外部链接

1 历史

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

2 非形式化的描述

在 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 表达式,就得到了组合子逻辑

3 形式化的定义

3.1字母表

lambda演算系统中合法的字符如下:

1.    标识符 (identifier) 的可数无穷集合:{ x1,x2,x3,…}变元(变元的数量是无穷的,不能在有限步骤内穷举,这个很重要,后面有定理是根据这一点证明的)

2.    à 归约

3.     等价

4.    λ,(,)  (辅助工具符号,一共有三个,λ和左括号右括号)

    所有能够在lambda演算系统中出现的合法符号只有以上四种,其他符号都是非法的。例如λx.x+2,如果没有其他对于+符号的说明,那么这就是一个非法的λ演算表达式。

3.2 λ-项

λ-项在一些文章中也称为λ表达式(lambda expression),它是由上面字母表中的合法字符组成的表达式,合法的表达式组成规则如下:

1.   任一个变元是一个项

2.   若M,N是项,则(MN)也是一个项  (function application,函数应用)

3.   若M是一个项,而x是一个变元,则(λx.M)也是一个项  (function abstraction,函数抽象)

4.   仅仅由以上规则归纳定义得到的符号串是项

则所有的 lambda 表达式可以通过下述以 BNF 范式表达的上下文无关文法描述:

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

说明1:通常,lambda 抽象 (规则 2) 和函数作用 (规则 3) 中的括号在不会产生歧义的情况下可以省略。如下假定保证了不会产生歧义:

(1) 函数的作用是左结合的。

 (2) lambda 操作符被绑定到它后面的整个表达式。

例如,表达式 ((λ x. (x x)) (λ y. y)) 可以简写成 (λ x. x x) λ y.y

说明2:(λx.M)这样的λ-项被称为函数抽象,原因是它常常就是一个函数的定义,函数的参数就是变量x,函数体就是M,而函数名称则是匿名的。用一个不恰当的比喻来说,我们通常认为的函数f(x)=x+2,可以被表达为λx.x+2。因为+是未定义的,所以这个比喻只是为了方便理解而已。

说明3:MN这样的λ-项被称为函数应用,原因是它表达了将M这个函数应用到N这个概念。沿用上面的例子f(x)=x+2,那么f(2)=2+2;同样的λx.x+2表达了f(x)的概念,那么(λx.x+2)2表达了f(2)的概念。其中M=λx.x+2,N=2,所以MN=(λx.x+2)2。

说明4:注意说明3只是为了方便理解,但是还存在很多与直观理解不符合的地方。例如xy也是一个合法的λ-项,它们也是MN形式的,不过x和y都仅仅是一个变量而已,谈不上函数代入。

说明4:这里的另一难点就是要区分变量和元变量,如:M=λx.x,这里x为变量,是我们在语法系统中定义了的,而M为元变量,没有在语法系统中定义,是我们用来表示的助记符。我们必须根据上下文来区分谁是变量,谁是元变量。

上面是λ-项的形式化定义,有一些是可以与函数理论直观联系的,而另一些只是说明这个λ-项是合法的,不一定有直观的字面意义。

3.3 公式

若M,N是λ-项,则MàN,M N是公式。

3.4 λ-项中的变量自由出现法则

类似 λ x. (x y) 这样的 lambda 表达式并未定义一个函数,因为变量 y 的出现是自由的,即它并没有被绑定到表达式中的任何一个 λ 上。在一个λ-项中,变量要么是自由出现的,要么是被一个λ符号绑定的。还是以函数的方式来理解变量的自由出现和绑定。例如f(x)=xy这个函数,我们知道x是和函数f相关的,因为它是f的形参,而y则是和f无关的。那么在λx.xy这个λ-项中,x就是被λ绑定的,而y则是自由出现的变量。

直观的理解,被绑定的变量就是作为某个函数形参的变量,而自由变量则是不作为任何函数形参的变量。

Lambda变量绑定规则:

1.   在表达式x中,如果x本身就是一个变量,那么x就是一个单独的自由出现。

2.   在表达式λ x. E中,自由出现就是E中所有的除了x的自由出现。这种情况下在E中所有x的出现都称为被表达式中x前面的那个λ所绑定。

3.   在表达式(MN )中,变量的自由出现就是M和N中所有变量的自由出现。

另一种关于变量的自由出现的规则也许更直接:

1.   free(x) = x

2.   free(MN) = free(M) 萛/nfree(N)

3.   free(lx " M) = free(M) – {x}

为什么要花大力气来给出变量自由出现的规则,是因为后面的很多地方要用到变量的自由出现的概念。例如α-变换和β-归约。

例子:分析λf.λx.fx中变量的自由出现和绑定状况。

λf.λx.fx =λf.E, E=λx.fx

E=λx.A, A=A1A2, A1=f, A2=x

所以在A中f和x都是自由出现的,

所以E中x是绑定λ x

所以整个公式中f是绑定第一个λ f的。

一个不含自由变量的项称为封闭项;封闭项也称为组合子。最简单的组合子,称为恒等函数:id=λx.x

3.5 λ x的控制域

来看两个λ-项,λx.xx和λx.(xx)有何不同?根据左结合的法则,λx.xx=(λx.x)x,其中第一个x是被λ绑定的,而第二个x则是自由出现的。而λx.(xx)中两个x都是被λ绑定的。这表明了两个λ-项中λx的控制域是不同的。

我们知道谓词演算中量词也是有控制域的,λx的控制域与它们类似,这里就不给出详细的定义了。其实也很直观。

4 变换与等价关系

4.1 α-变换

α-变换规则表达的是,被绑定变量的名称是不重要的。比如说 λx.x 和 λy.y 是同一个函数。因此,将某个函数中的所有约束变量全部换名是可以的。尽管如此,这条规则并非像它看起来这么简单,关于被绑定的变量能否由另一个替换有一系列的限制需要遵循。

首先是一个说明:如果M,N是λ-项,x在M中有自由出现,若以N置换M中所有x的自由出现(M中可能含有x的约束出现),我们得到另一个λ-项,记为M[x/N]。

α-变换规则如下:

λx.M≌λy.M[x/y]  如果y没有在M中自由出现,并且只要y替换M中的x,都不会被M中的一个λ绑定。即:替换的变元不能在M中自由出现,并且也只替换M中的自由变元

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

注意:M[x/N]定义出来只是一种记号,它不等于α-变换。既M!≌ M[x/y]

例子:λx.( λx.x)x≌λy.(λx.x)y  但是 ( λx.x)x!≌(λx.x)y

α-变换主要用来表达函数中的变量换名规则,需要注意的是被换名的只能是M(函数体)中变量的自由出现。

4.2 β-归约

β-归约表达的是函数应用或者函数代入的概念。前面提到MN是合法的λ-项,那么MN的含义是将M应用到N,通俗的说是将N作为实参代替M中的约束变量,也就是形参。β-归约的规则如下:

(λx.M)N à M[x/N] 如果N中所有变量的自由出现都在M[x/N]中保持自由出现

注意: (lx. ly.(y x))y ?ly.(y y)是错的,应为y在ly.(y x)中不自由出现,这时要归约就应该先用α-变换把ly.(y x)中的y换成其他变元

β-归约是λ演算中最重要的概念和规则,它是函数代入这个概念的形式化表示。

一些例子如下:

(lx.ly.y x)(lz.u) ?ly.y(lz.u)

(lx. x x)(lz.u) ?(lz.u) (lz.u)

(ly.y a)((lx. x)(lz.(lu.u) z)) ?(ly.y a)(lz.(lu.u) z)

(ly.y a)((lx. x)(lz.(lu.u) z)) ?(ly.y a)((lx. x)(lz. z))

(ly.y a)((lx. x)(lz.(lu.u) z)) ?((lx. x)(lz.(lu.u) z)) a

需要多加练习才能习惯这种归约。

4.3 等价关系

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

f g:当且仅当(1)g≌f ;(2)g甪 or f甮 ;(3)存在h,f h且h g之一成立。

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

4.4 η-变换

前两条规则之后,还可以加入第三条规则,η-变换,来形成一个新的等价关系。η-变换表达的是外延性的概念,在这里外延性指的是,两个函数对于所有的参数得到的结果都一致,当且仅当它们是同一个函数。

η-变换:λ x . f x  f,只要 x 不是 f 中的自由出现。

f 与 g 外延的等价:对任意lambda 表达式 a,如果有f a  g a成立,则f  g也成立。

下面说明了为何这条规则和外延性是等价的:(即由η-变换可以证明外延等价,相反由外延等价也可以证明η-变换)

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

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

5 λ演算的操作语义

如果一个λ-项M中不含有任何形为((λx.N1)N2)的子项,则称M是一个范式,简记为n.f.。如果一个λ-项M通过有穷步β-归约后,得到一个范式,则称M有n.f.,没有n.f.的λ-项称为n.n.f.。

    通俗的说法是,将一个λ-项进行β-归约,也就是进行实参代入形参的过程,如果通过有穷步代入,可以得到一个不能够再进行代入的λ-项,那么这就是它的范式。如果无论怎样代入,总存在可以继续代入的子项,那么它就没有范式。

例子

M = λx.(x((λy.y)x))y,则Mà y((λy.y)y) à yy。M有一个n.f.。

例子

M =λx.(xx) λx.(xx),则Màλx.(xx) λx.(xx)=M。注意到M的归约只有唯一的一个可能路径,所以M不可能归约到n.f.。所以M是n.n.f.。

注意这个λx.(xx) λx.(xx)在λ演算的协调性研究中是一个比较经典的项。((λ x. x x) (λ x. x x))被称为Ω, ((λ x. x x x) (λ x. x x x))被称为 Ω2。

一般的把一个λ-项规约为范式的过程称为求值,范式也叫做值。在《类型和程序设计语言》一书中,作者给出了λ-演算中的几种不同的求值策略。每个策略确定一个项在下一步求值中激活哪一个约式或几个约式。

注意到在求值的过程中是左结合,并且当且仅当左边以是值了才能对右边的项进一步求值      

6 λ演算的公理化和定理

6.1 λ演算的公理

这一段来自《数理逻辑与集合论》(第二版 清华大学出版社)。

1.         (λx.M)N à M[x/N] 如果N中所有变量的自由出现都在M[x/N]中保持自由出现

2.         MàM

3.         MàN, NàL => MàL 

4.         MàM’=>ZMàZM’

5.         MàM’=>MZàM’Z

6.         MàM’=>λx. M àλx. M’

7.         MàM’=>M M’ 

8.       M M’=>M’ M

9.         M N,N L=>M L

10.     M M’ => ZM  ZM’

11.     M M’ => MZ  M’Z

12.     M M’ =>λx. M λx. M’

13.M≌N => MàN 如果y没有在M中自由出现,并且只要y替换M中的x,都不会被M中的一个λ绑定。

如果某一公式MàN或者M N可以用以上的公理推出,则记为λ├MàN和λ├M N。

规则13可以加也可以不要,要了表示是考虑α-变换的系统

6.2 不动点理论

Λ表示所有的λ-项组成的集合。

定理:对于每一个F∈Λ,存在M∈Λ,使得λ├FM=M。

证明:定义w=λx.F(xx),又令M=ww,则有λ├M=ww=(λx.F(xx))w=F(ww)=FM。

证明是非常巧妙的,对于每个F,构造出的这个ww刚好是可以通过一次β-归约得到F(ww)=FM,如果再归约一次就得到F(FM),这样可以无限次的归约下去得到F(F(F(F…(FM)…)))。

6.3 Church-Rosser定理及其等价定理

这两个定理告诉我们这样一个事实:如果M有一个n.f.,则这个n.f.是唯一的,任何β-归约的路径都将终止,并且终止到这个n.f.。

Church-Rosser定理:如果λ├M N,则对某一个Z,λ├MàZ并且λ├NàZ。

与之等价的定理如下,

Diamond Property定理:如果MàN1,MàN2,则存在某一Z,使得N1àZ,N2àZ。(规约的合流性)

推论:MàN1,MàN2 且N1,N2都是范式 则N1≌N2 (范式在α-变换下的唯一性)

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

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

7 lambda 演算中的程序设计

7.1 自然数与运算

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

0 = λ s. λ z. z

1 =λ s. λ z.. s z

2 =λ s. λ z. s (s z)

3 =λ s. λ z. s (s (s z))

以此类推。直观地说,lambda 演算中的数字 n 就是一个把函数 f 作为参数并以 fn 次幂为返回值的函数。换句话说,Church 整数是一个高阶函数 -- 以单一参数函数 f 为参数,返回另一个单一参数的函数。

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

SUCC1 = λ n. λ s. λ z. s(n s z)

SUCC2 =  λ n. λ s. λ z. n s(s z)

加法是这样定义的:

PLUS = λ m. λ n. λ s. λ z. m s (n s z)

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

PLUS 2 3    与    5

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

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

MULT2 =λ m. λ n. λ s. λ z. m (n s) z

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

MULT3 = λ m. λ n. λ s. m (n s)

幂的定义:

       POWER1 = λ m. λ n. m (MULT n) 1

       POWER2 = λ m. λ n. m n  (可能有点问题,我验证了好象不正确)

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

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

或者

PRED = λ n. ng. λ 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。

   在《类型和程序设计语言》一书中作者对于数和各种运算的编码描述得比较清晰,强烈建议看以下,并且作者还介绍了直接基于原始的布尔类型和数型的系统,简称为λNB,并给出了这两套系统之间的转换。

7.2 逻辑与断言

习惯上,下述两个定义 (称为 Church 布尔值) 被用作 TRUE 和 FALSE 这样的布尔值:

tru = λ u. λ v. u

fls = λ u. λ v. v

这样我们可以定义一个组合式test=λl.λm.λ n.lmn,使得b为tru时,test b v w规约为v,而当b为fls时,test b v w规约为w。

并且我们还可以定义逻辑连接词:

and = λb. λc. b c fls

or =λb. λc. b c tru

not =λb. b fls tru

断言是指返回布尔值的函数。最基本的一个断言 ISZERO,当且仅当其参数为零时返回真:

ISZERO = λ n. nx. fls) tru

断言的运用与上述 TRUE 和 FALSE 的定义,使得 "if-then-else" 这类语句很容易用 lambda 演算写出。

7.3 递归  

递归是一种以函数自身迭代自身变元的算法,一般是通过函数自身来定义函数的方式实现。表面看来 lambda 演算不允许递归,其实这是一种对递归的误解。考虑阶乘函数 f(n) 一般这样递归地定义:

f(n) = 1, 若 n = 0; n·f(n-1), 若 n>0.

λ语言:

FACT = λ n. nu. MULT n (FACT (PRED n))) 1

用 Y-组合子 在 λ语言 中合法地定义:

FACT = Y (λ g. λ n. nu. MULT n (g (PRED n))) 1)

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

这一节我也没分析透彻,只能原本引用,并且以上的内容也只能当作一个不严格的了解,暂时还不知道其有没有错,不过一点可以肯定的是上面定义的Y-组合子只能用于按名调用的情况,在按值调用的形式中是无用的,因为对任何g,表达式Yg发散。想要深入理解还是参看一下《类型和程序设计语言》这本书,不过书中也没深入的具体分析按值和按名调用的作用方式。其实这涉及到不同的操作语义的定义。想要在这里弄透还得找找论文看看。

8 参考文献和外部连接

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值