Lamda 表达式(λ-calculus)学习(1)

(参考:《type and Programming Languages》)

一、语法:

t ::=    terms:

  x    variable

  λx.t    abstraction

  t t    application

λx.t 可理解为:一个函数,以变量x为参数,返回term t。t是该抽象的body。(lamda表达式的body总是延伸得尽可能长)

l 若x在λx.t 的t中出现,称x由该抽象约束(bind),称λx是一个范围为t的binder

l 若x出现在某位置,且没有被一个封闭的x上的抽象约束,则称x是自由的free)。 例如:x y 和 λy. x y 中的x

l 若一个term中不含自由的变量,则称之为闭合的(closed),或称之为一个combinator

二、操作的语法:

在上面的语法定义中没有内建常数和基本运算符等等,因此唯一的“计算”就是将函数作用于参数(同样也是函数)。

一步计算相当于将一个application重写:用其右手边的元素替代其左手边的abstract的body中的bound variable。即:

其中右边部分表示:将t1中所有的自由的x替换为t2。

例:(λx.x) y → y

(λx.x (λx.x))(u r) → u r (λx.x)

【因为左边的抽象的body: x (λx.x) 中,只有第一个x是自由的。】

形如的term称为redex("reducible expression");而使用上面的规则重写一个redex,称为beta-reduction。

三、lambda-calculus的求值策略:

1.full beta-reduction:any redex may be reduced at any time。

2.normal order strategy:最左侧的、最外侧的redex总是被优先化简/归约。

3.call by name strategy:不允许在抽象内部的归约。

4.call by value strategy:使用最广泛。(only outermost redexes are reduced?)

四、用Lamda表达式编程

1. 多个参数:使用高阶函数。

例如以一个值对为参数,返回s的函数 f = λ(x, y).s 可写为 f = λx.λy.s ,其含义为“f是一个函数:传入参数u作为x、返回一个传入参数v作为y返回结果s的函数”。则 f u v => λy.[x → u]s v => [y → v][x → u]s

2. Church Booleans

定义term tru和fls如下(区别于常量true和flase):

tru = λt. λf. t;

fls = λt. λf. f;

则tru v w 返回v;fls v w 返回w。

则可以定义布尔操作如: and = λa. λb. a b fls,则仅在u、v均为tru时 and w v 返回tru,否则返回fls。

3. Pairs

我们可将值对定义为term:

pair = λa. λb. λf. f a b; 这里f应是tru或fst

fst = λp. p tru; 这里的p都是pair

snd = λp. p fls;

例: fst (pair v w) → fst (λb. b v w) → (λp. p tru) (λb. b v w) → (λb. b v w) tru → tru v w → v

4. Church Numerals

定义Church Numerals如下:

c0 = λs. λz. z;

c1 = λs. λz. s z;

c2 = λs. λz. s (s z);

c3 = λs. λz. s (s (s z));

...

每个数字n表示为一个combinator cn :传入两个参数s和z(分别代表"successor"和"zero"),将s作用于z上n次。

则我们可定义successor函数如下:

scc = λn. λs. λz. s (n s z);

scc是一个combinator,传入一个Church numeral n,返回其后继。

类似的,可定义:

plus = λm. λn. λs. λz. m s (n s z);

times = λm. λn. m s (plus n) c0;

iszro = λm. m (λx. fls) tru;

减法的实现较复杂,通过predecessor function:传入c0返回c0、传入ci+1返回ci。函数ss以一个pair ci cj为参数,返回pair cj cj+1。因此将ss m次应用于pair c0 c0可得到pair c0 c0(当m=0)或pair cm-1 cm(当m>0)。

zz = pair c0 c0;

ss = λp. pair (snd p) (plus c1 (snd p));

prd = λm. fst (m ss zz);

6. Enriching

从前面可以看出,布尔值、数字以及其上的操作可以由纯lamda表达式编码。为了便捷可引入基本的布尔和数值类型。将丰富后的系统称为λNB。在λNB中的两种表达方式可互相转换。要将一个Church boolean转化为一个基本布尔值,只需将其作用于true和false:

realbool = λb. b true false;

而反方向的转化则通过if表达式:

churchbool = λb. if b then tru else fls;

而Church numeral上的一个返回真布尔值的equality function可写为:

realeq = λm. λn. (equal m n) true false;

类似的,将一个Church numeral转化为一个对应的基本数值,通过将其作用于succ和0:

realnat = λm. m (λx. succ x) 0;

7. Recursion

前面讲到,不能再应用求值法则的term称为normal form。然而一些term并不能被求值到normal form。例如下面的divergent combinator:

omega = (λx. x x) (λx. x x);

只含有一个redex,而将其归约后得到的结果仍是omega 本身。

没有normal form的term称做diverge。

omega combinator可一般化地称为fixed-point combinator(call-by-value Y-combinator),可用以定义递归的函数,例如下面的factorial函数:

fix = λf. (λx. f (λy. x x y)) (λx. f (λy. x x y));

时间: 2024-08-08 19:31:59

Lamda 表达式(λ-calculus)学习(1)的相关文章

Lamda 表达式学习(2) ——lamda 变换

回顾λ-calculus语法: t ::=  terms: x  variable λx.t  abstraction t t  application λ-变换:保持λ-项含义的同时对其进行变换. 1. α-变换:改变被绑定变量的名称,所代表的含义仍是一样的. λx.t → λy.t(x:=y) (将表达式的body t中的所有x的自由出现替换称y) 两个lamda表达式如果可以通过α-变换(可能应用到子项)从一个变换到另外一个,则称他们是全等的. 2. β-规约:对application的变

C#学习记录(七)LINQ语句及LAMDA表达式

LINQ LINQ是一种集成在计算机语言里的信息查询语句,可以让编写者以代码的方式操作数据库. 在C#中,LINQ语句有两种写法. 这是第一种写法 IEnumerable<Customer> result = from customer in customers where customer.FirstName == "Donna“ select customer; 由于这种写法比较容易和SQL语言混淆,所以我更倾向于使用另一种写法 IEnumerable<Customer&g

[.net 面向对象程序设计进阶] (5) Lamda表达式(二) 表达式树快速入门

[.net 面向对象程序设计进阶] (6) Lamda表达式(二) 表达式树快速入门 本节导读: 认识表达式树(Expression Tree),学习使用Lambda创建表达式树,解析表达式树. 学习表达式在程序设计中的优点:比如构造动态查询.动态构造表达式树完成未知对象属性访问,比反射的性能高出很多.我们可以说表达式树才是Lambda的精髓,是我们必须要熟练掌握并灵活运用的. 1.关于表达式树(Expression Tree) 表达式树以树形数据结构表示代码,其中每一个节点都是一种表达式,比如

[.net 面向对象程序设计进阶] (5) Lamda表达式(一) 创建委托

[.net 面向对象程序设计进阶] (5) Lamda表达式(一)  创建委托 本节导读: 通过学习Lambda表达式,学会创建委托和表达式目录树,深入了解Lambda的特性,让你的代码变的更加清晰.简洁.高效. 读前必备: 本节学习前,需要掌握以下知识: A.泛型        (请参考[.net 面向对象编程基础]  (18) 泛型) B.Linq基础 (请参照[.net 面向对象编程基础] (19) LINQ基础) C.Linq使用  (请参照[.net 面向对象编程基础]  (20) L

[.net 面向对象程序设计进阶] (7) Lamda表达式(三) 表达式树高级应用

[.net 面向对象程序设计进阶] (7) Lamda表达式(三) 表达式树高级应用 本节导读:讨论了表达式树的定义和解析之后,我们知道了表达式树就是并非可执行代码,而是将表达式对象化后的数据结构.是时候来引用他解决问题.而本节主要目的就是使用表达式树解决实际问题. 读前必备: 本节学习前,需要掌握以下知识: A.继承 [.net 面向对象编程基础]  (12) 面向对象三大特性——继承 B.多态 [.net 面向对象编程基础]  (13) 面向对象三大特性——多态 C.抽象类 [.net 面向

java8-详解Lamda表达式

一回顾与说明 ????通过之前发布的"Java8Lamda和Stream原理引入"一文章中你已经了解了为什么会有Lamda表达式的由来,Lamda表达式的基本语法等:Lamda表达式简而言之:Lamda表达式本质上就是匿名类,我们称Lamda表达式就是函数式接口的实现: 二Lamda表达式的结构与函数式接口的关系 1准备工作 例子还是之前文章关于车的例子 判断型函数式接口: /** * @Author lsc * @Description <p> </p> *

JAVA8新特性——Lamda表达式

JAVA9都要出来了,JAVA8新特性都没搞清楚,是不是有点掉队哦~ Lamda表达式,读作λ表达式,它实质属于函数式编程的概念,要理解函数式编程的产生目的,就要先理解匿名内部类. 先来看看传统的匿名内部类调用方式: interface MyInterface{ void lMethod(); } public class Main { public static void test(MyInterface myInterface){ myInterface.lMethod(); } publi

Java8 Lambda表达式深入学习(4) -- Java8实现方式

前几篇文章讨论了函数式接口和Lambda表达式语法.invokedynamic指令,以及Groovy2如何利用indy指令.本篇文章在前面几篇的基础之上,简要介绍Java8底层是如何实现Lambda表达式的. 示例代码 本文将以下面的代码为例展开讨论: import java.util.Arrays; import java.util.List; public class LambdaImplTest { public static void main(String[] args) { m1(A

匿名方法与Lamda表达式

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 匿名方法与Lamda表达式 { class Program { static void Main(string[] args) { //任何可以使用委托的地方都可以使用匿名方法,匿名方法就是没有名字的方法. //a指向一个匿名方法 Action a