文档库 最新最全的文档下载
当前位置:文档库 › Lambda

Lambda

Lambda
Lambda

Lambda演算

Lambda演算是一个形式系统,它被设计出来用来研究函数定义,函数应用和递归。它是在二十世纪三十年代由Alonzo Church 和 Stephen Cole Kleene发明的。Church在1936年使用lambda演算来证明了判定问题是没有答案的。Lambda演算可以用来清晰的定义什么是一个可计算的函数。两个lambda演算表达式是否相等的问题不能够被一个通用的算法解决,这是第一个问题,它甚至排在停机问题之前。为了证明停机问题是没有答案的,不可判定性能够被证明。Lambda演算对于函数式编程语言(例如lisp)有重大的影响。

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

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

这里不由得想起一位我尊敬的老师的博士毕业论文就是关于π-演算的,可惜这位老师已经去别的学校了。

Lambda演算表达了两个计算机计算中最基本的概念“代入”和“置换”。“代入”我们一般理解为函数调用,或者是用实参代替函数中的形参;“置换”我们一般理解为变量换名规则。后面会讲到,“代入”就是用lambda演算中的β-归约概念。而“替换”就是lambda演算中的α-变换。Lambda演算系统的形式化定义

维基百科全书上面的对于lambda演算的定义不是很正规,但是说明性的文字较多。而数理逻辑中的定义很严密,不过没有说明不容易理解。我尽可能把所有资料结合起来说明lambda演算系统的定义。字母表

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

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

2. à 归约

3. =等价

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

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

λ-项

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

1. 任一个变元是一个项

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

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

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

说明1:λ-项是左结合的,意思就是若f x y都是λ-项,那么f x y=(f x) 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都仅仅是一个变量而已,谈不上函数代入。

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

公式

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

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

在一个λ-项中,变量要么是自由出现的,要么是被一个λ符号绑定的。还是以函数的方式来理解变量的自由出现和绑定。例如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) è free(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的。

λ x的控制域

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

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

α-变换(α-conversion)

α-变换规则试图解释这样一个概念,λ演算中约束变量的名称是不重要的,例如λ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中的一个λ绑定。

例子:λx.( λx.x)x = λy(λx.x)y

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

β-归约

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

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

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

一些例子如下:

(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

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

η-变换(η-conversion)

η-变换表达了“外延性”(extensionality)的概念,在这种上下文中,两个函数被认为是相等的“当且仅当”对于所有的参数,它们都给出同样的结果。我理解为,对于所有的实参,通过β-归约都能得到同样的λ-项,或者使用α-变换进行换名后得到同样的λ-项。

例如λx.fx与f相等,如果x没有在f中自由出现。

λ演算的公理和规则组成

这一段来自《数理逻辑与集合论》(第二版清华大学出版社)。还修正了其中的一个错误。

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’

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

范式

如果一个λ-项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。

不动点理论

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

定理:对于每一个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)…)))。

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。

后记

最后两个定理的证明没有怎么看懂,所以没有在笔记中记下。另外,前天在网上订购的《程序设计语言:概念和结构》第二版刚刚拿到手,其中有关于λ演算的一章。应该也对我大有帮助,待看完再说。

学习λ演算的初衷是了解一些程序设计的最基本原理,没想到最后还是绕到形式系统和数理逻辑这儿来了。不过,形式化表达的λ演算更清晰。

国内大学虽然也开程序设计的课程,不过好像都是pascal,c/c++之类,关于程序设计的本质基础的程序好像并不多。随着大学扩招,中国有望在很短的时间内成为世界上程序员最多的国家,如果仅仅只学命令式和面向对象的程序设计,一定是不够的。函数式和逻辑式程序设计语言也是要学学的啊。文中肯定有很多错漏,希望看出来的人给我留言。

参考文献

School of Computer Science and Software Engineering, Faculty of Information Technology, Monash University, Australia 3800

这个大学的FP课程中的Lambda演算相关部分

https://www.wendangku.net/doc/7513043544.html,.au/~lloyd/tildeFP/Lambda/Ch/

wikipedia中lambda演算相关介绍

https://www.wendangku.net/doc/7513043544.html,/wiki/Lambda_calculus

《数理逻辑与集合论》第二版清华大学出版社

Lambda算子

Currying

据说Currying翻译为局部套用函数,也不知真假。喜欢吃印度美食的老大们不要激动。Currying 和咖喱没有半点关系。这个技巧以逻辑学家Haskell Curry的姓命名。Haskell Curry也是名动一时的人物。他和Moses Sch?nfinkel共创了组合逻辑(combinatory logic),并把这们学科发扬光大。当初Curry搞出组合逻辑,主要是为了在数理逻辑里避免使用变量。后来搞函数编程的人们发现,组合逻辑是一类函数编程语言的理论基础。一些函数语言里常见的特性,比如说高阶函数合lazy evaluation, 就是用组合逻辑里的combinator实现的。当初Alanzo Church对这个理论也相当熟悉。难说lambda理论不是受了组合逻辑的影响。大牛Philip Wadler为了纪念Curry, 把他的函数语言叫做Haskell。Haskell也是一门巨酷的函数语言,兼顾数学的优美和软件开发的实用性。连LInspire的开发组都决定用Haskell作为系统开发的语言(但我很奇怪他们为什么放弃使用另一门酷酷的函数语言Ocaml)。说远了。

解决参数限制的关键在于认识到函数也是数据(用更严格的说法,是值)。既然是数据,就可以传来传去。如果有两个参数,我们可以写一个接受第一个参数的函数,而这个函数返回的是接受第二个参数的函数。―就那么简单!我们在JavaScript里不是常用这个功能?‖ 嘻嘻,我们在JavaScript里的确常用这个功能。JavaScript其实是带C句法的函数语言,支持高阶函数,自然支持Currying。JavaScript的功能其实颇为强大,不然Douglas Crockford不会说JavaScript是最被人误解的语言。

举例来说,假设我们要写一个函数,把x和y相加。最自然的写法是lambda x y . plus x y. 既然我们只能一次接受一个参数,我们可以先写一个接受x 的函数。这个函数返回一个接受y 的函数。这个被返回的函数把x 和y 相加:lambda x.(lambda y. plus x y)。简单吧?数学奇妙之处就在于我们用极为简单的砖块搭建出恢弘的宫殿。事实上,数学家们总是极力追求理论基础的简洁。他们不知疲倦地挥舞着奥卡姆剃刀,直到把自己的理论切割成东家之子:增之一分则太长,减之一分则太短。有了Currying这个工具,我们可以放心使用多参数的标记了。反正多参数的lambda不过是单参数lambda的方便用法而已,没有任何实质上的改变。

自由vs 有界标识符

标识符和变量其实是一个意思。我记得国内教材里很少用标识符这个说法。不过既然原作者用这个说法,我就跟着用了。上次说到Currying解决了如何处理多参数的问题。在讨论怎么使用lambda前,我们还要解决一个细微但重要的语法问题:封闭(closure),或者叫完全有界(complete bounding)。这里的有界和一阶谓词逻辑里的有界没有本质区别,对一阶谓词逻辑熟悉的老大们可以放心跳过。其实有界涉及的定义很直观,我们看一个例子先。假设我们有一个函数lambda x y. (lambda y. y + 2) + x + y +z,lambda y. y+2里的y和它后面的y是不是一样的呢?显然它们是不一样的。为了处理这种区别,我们引入了有界。当一个lambda表达式被计算时,它不能处理无界的标识符。当一个标识符出现在一个lambda表达式的参数里,并且被包含在这个lambda表达式里,我们就可

以说这个标识符有界。如果一个标识符没有被包含在任何一个表达式里,我们就叫它为自由变量。比如说,上面那个lambda表达式里,x 出现在lambda x y .(....)里,所以它是有界的变量,它的包含环境(enclosing context,用―语境‖或者―上下文‖怎么听怎么别扭,好像俺是《读书》的御用作者似的。:-D)是整个lambda表达式。lambda y. y+2里的y也是有界的,但它的包含环境是lambda y. y+2。标识符z没有出现在包含它的表达式的参数列表里,所以是自由变量。再举几个例子:

https://www.wendangku.net/doc/7513043544.html,mbda x . plus x y: 这个表达式里,"y"和"plus"都是自由变量,因为它们不是任何

包含它们的表达式的参数。x有界,因为它被包含在plus x y里,而plus x y的参数有x。

https://www.wendangku.net/doc/7513043544.html,mbda x y.y x: 这个表达式里,x和y都有界,因为它们是这个表达式的参数。

https://www.wendangku.net/doc/7513043544.html,mbda y . (lambda x . plus x y): 在内嵌的表达式lambda x. plus x y里,y

和plus 是自由变量而x是有界变量。在整个lambda表达式里,x和y都有界:x在内嵌表达式界内,而y在整个表达式界内。plus仍然自由。

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

一个lambda表达式完全合法仅当它的所有变量都有界。不过当我们考查某个复杂表达式里的子表达式且不考虑上下文时,那些子表达式可以有自由变量-其实确保正确处理那些子表达式里的自由变量非常重要。

Lambda 算子计算规则

其实真正的规则就俩:alpha和beta。Alpha规则又叫转换(conversion)规则,而beta规则又叫简化(reduction)规则。

Alpha转换

这个充满了《星际迷航》味道的规则其实就是重命名操作。它无非是说变量名不重要:给定任何一个lambda表达式,我们可以任意改变参数的名字,只要我们相应地改变这些对应这些参数的变量名字。

比如说,我们有如下表达式:

lambda x . if (= x 0) then 1 else x^2

我们通过alpha规则把X改成Y(写作alpha[x/y], 和逻辑里的变量替换一个写法),于是得到:lambda y . if (= y 0) then 1 else y^2

Alpha操作完全不影响lambda表达式的意义。不过我们后面会发现,这个操作很重要,因为它让我们能够实现诸如递归的操作。

Beta简化

Beta简化就有意思了。我们只需要这一个规则,就可以让lamdba算子实现一台计算机能做的任何计算。透过纷繁的表象,我们会发现事情的本质往往出人意料地清晰而简单。删繁为简,恰是数学魅力所在。

Beta规则无非是说,应用一个函数(也就是lambda表达式。一个意思)等价于通过把函数体内有界的变量替换成应用里对应参数的实际值来替换原来的函数。听上去有些拗口(呵呵,其实原文更拗口),但当你看一个例子就知道它其实很简单:

假设我们要应用一个函数:"(lambda x . x + 1) 3"。Beta规则说,我们可以替换整个表达式,把函数体(也就是―x+1‖)里的参数对应的x替换成实际的值3。所以最后的结果是―3+1‖。

再来一个稍微复杂点的例子:

lambda y . (lambda x . x + y)) q

这个表达式有意思,因为应用了这个表达式后,我们可以得到另外一个表达式。也就是说,它是一个生成表达式的表达式(说到这里,玩儿动态语言的老大们可以笑了,玩儿C/C++/Java的老大们可以流口水了)。当我们对这个表达式应用Beta简化时,我们把所有对应参数y的变量替换成实际的值q。所以结果是"lambda x, x+q"。

再来一个例子:

"(lambda x y. x y) (lambda z . z * z) 3". 这个带两个参数的函数把第一个参数应用到第二个参数上。当我们计算它的值时,我们把第一个lambda表达式里的变量x换成lambda z. z * z, 再把变量y换成3,得到(lambda z. z * z) 3。对该结果应用Beta简化,我们得到3 * 3。

Beta的严格定义如下:

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转换,是的重名的变量不再重名。形式化定义不够直观,直观描述又不够简洁。还是来个例子漱漱口:

给定一个表达式,lambda z. lambda x. x+z. 假设我们要应用这个表达式:

(lambda z . (lambda x . x + z)) (x + 2)

在实际参数"(x + 2)"里,x是自由变量。但x不是表达式lambda x. x+z的自由变量。也就是说,free(e) \subset free(B[ x:=e])不成立。如果我们打破Beta简化的规则,直接开始Beta简化,便会得到: lambda x . x + x + 2

"x+2"里自由变量,x,现在变得有界了。如果我们把结果应用到3上:(lambda x. x+2+2) 3,我们得到3 + 3 + 2。

如果我们按正常程序办事呢?

应用alpha[x/y]: lambda z . (lambda y . y+z)) (x + 2)

应用beta: lambda y . y + x + 2) 3

再次应用beta: 3 + x + 2.

"3+x+2" 和"3+3+2" 很不一样哈!

规则就这些了。我们还可以选择性地加一个所谓的Eta-化简,不过它不是必需的。我们就此跳过。我们讨论的这套系统已经是图灵完备的计算体系。那这套系统到底有什么用嗫?到底怎样才能让这套系统变得真正有用嗫?嗯,要说明这些问题,我们得先定义一些基本的函数,以便我们做算术,条件测试,递归,等等。这些会在以后的帖子里谈到。

我们也还没有谈到适合lambda算子的模型(Good Math Bad Math的作者在这里和这里讨论了模型)。模型也是很重要的东西。逻辑学家用了好几年时间研究lambda算子,才搞出一个完备的模型。而且早先时候,尽管lambda算子看起来没错,为它制订模型的工作却失败了。这在当时极为引人关注。要知道,毕竟一个系统没有有效的模型就没有实际的意义。

阿隆佐.丘齐(Alonzo Church)的天才:Lambda算子里的数

前面建立了lambda运算的基本规则,就可以用lambda算子做点有意思的东西了。开始前为方便计,我们先来点语法糖花差花差,用来命名函数。这些语法糖可以让复杂的公式好写一点。

我们用"let" 来引入一个―全局‖函数(也就是说,我们用这个函数时,不用在每个表达式里定义一次):let squer = lambda x. x^2

这个式子申明了一个叫"square"的函数,定义为lamdba x. x^2。如果我们有一个表达式"square 4",上面的"let"意味着这个表达式和下面这个表达式一样:

(lambda square. square 4)(lambda x. x^2)。这个"let"是从Common Lisp或者Scheme里借来的。Lambda算子里可没有这个东西。数学家推崇―如无必要,毋增实体‖。这些关键字不入他们的法眼。不过对写惯了程序的我们来说,这些句法糖就可爱多了。

我们的例子里会用到数字和算术操作符。不过记住lambda算子里根本没有数字。我们只有函数!所以我们需要发明用函数来创造数字的方法。幸好Alonzo Church是个天才。他既然发明了lambda 算子,用lambda算子表征数字自然不在话下。他搞出的用于数字的函数自然就叫做丘齐数(Church Numerals)。

丘齐数里,所有的数字都是两个参数的函数:

1.零是lambda s z . z

2.一是lambda s z . s z

3.二是lambda s z . s (s z)

4.对任意一个数"n",它的丘齐数都是一个函数。这个函数把它的第一个参数应用到第二个参

数上n次。用流行的写法,就是lambda s z . s s n z。绕口啊绕口。做形式化的东东不幸之处就是成天和绕口令打交道。解脱这道呢?当然就是牢记牛人费因曼在Connection Machine工作时的学习方法:问最简单的问题。―给我最简单的例子‖。―怎么才能验证这是正

确的?‖。

比如说零(lambda s z . z)吧,第一个参数是s, 应用零次就是没有,所以函数体就是孤零零的"z"。那数字一呢?当让就是把第一个参数,s,应用到z上一次,所以函数体就变成了"s z"。

理解这个定义的方法之一时把"z"看作丘齐数里零的名字,而把"s"看后继函数(successor function)的名字。―后继函数‖其实很简单,C/C++里的++是也。所以呢,零就是一个返回"0"这个值的函数;一就是把后继函数应用到零上一次的函数;二就是把后继函数应用到一上一次或者说零上两次的函数。0++ 得到1, 1++ 等价与(0++)++,而1++得到2.现在把0换成z,把++换成s, 一切就清楚了。

现在--看好了。如果我们想做加法,x+y,我们需要一个带4个参数的函数。两个参数代表相加的两个数字,以及为得到结果而需要的"s"和"z"。

let add = lambda s z x y . x s (y s z)

看着好像有点不知所云。不过我们可以用Curry这个利器,分开"s" "z"和x, y。首先,Curry后得到的函数带两个参数,x和y(这个好比add(x, y),符合我们对加号的理解)。其次,我们需要正规化x和y需要的s和z,让x和y共享相同的零和后继函数的绑定:

let add = lambda x y. (lambda s z . (x s (y s z)))

仔细观察一下,上面的式子无非是说,要把x和y相加,我们先用"s"和"z"创建丘齐数"y",然后在把"x"应用到y上。应用时需要的"s"和"z"是"y"里的"s"和"z"。也就是说,我们的到的结果是一个函数,这个函数把自己加到另一个函数上。还是用例子来说明问题。比如说2+3:

add (lambda s z. s (s z)) (lambda s z . s (s (s z))) news newz

为了让演算变得稍微容易一点,我们先对2和3来个Alpha转换。让2用s2和z2,而3用s3和z3:

add (lambda s2 z2 . s2 (s2 z2)) (lambda s3 z3 . s3 (s3 (s3 z3)))

现在我们可以把"add"替换成它的定义了:

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

现在可以对"add"用beta变换了(温馨提示:也就是把形参x和y换成对应的实参):

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

然后我们可以对3这个丘齐数做beta转换。这步操作其实是―正规化‖3:把3的定义里的后继函数和零函数(还记得零是个函数吧?)替换成add的参数列表里的后继函数和零函数:

lambda s z . (lambda s2 z2 . s2 (s2 z2)) s (s (s (s z)))

嗯,有点眉目了。现在是真正漂亮的地方了。再来次对2的Beta变换。看看我们准备做什么:2是个带两个参数的函数:一个参数是后继函数,另一个是零函数。要把2加到3上,我们只需要用到"add"这个函数的后继函数。也就是说,我们把计算了3后的结果当成零函数的值!

lambda s z . s (s (s (s (s z)))

而这个式子,正是丘齐数5!

丘齐数酷的地方是它抛弃了传统整数的概念,用函数取而代之。它把每个数对应为一个函数。而数数(counting)这个操作被对应为应用某个函数(在这里是后继函数)的次数。当然了,上面的介绍非常简单。对丘齐数感兴趣的,可以看这篇文章。

丘齐数对编程有什么用嗫?俺还真不知道。但丘齐数(进而到丘齐编码)确实一系列基础理论中有重要应用,比如说有类型的lambda算子。不过这点重要吗?不重要吗?重要吗?不重要吗?研究研究嘛。

Lambda算子里的布尔值和选择

原文在这里。既然Lambda算子里有了数的概念,我们想进行任意的计算就只需要两件东西了:怎么表示选择,和怎么表达重复操作。我们先聊聊怎么表示布尔值(也就是非真即假的二元集合)和选择,然后再讨论重复和递归(友情预告:人见人爱的Y Combinator终于可以出场了)。

我们一般把选择表示为if/then/else的表达式,和大多数编程语言的选择语句没有区别。丘齐数的基本模式无非是把一个数表达为一个函数。这个函数把它自己加到另外一个函数上。我们继续沿用这个模式,把true和false也表达为对自己的参数执行if-then-else操作的函数:

let TRUE = lambda x y . x

let FALSE = lambda x y . y

现在我们就可以写“if-then-else”函数了(记到哈,lambda算子理论里所有东东都是函数)。这个函数的第一个参数是一个条件表达式,第二个参数是当第一个参数为真时返回的表达式,而第三个参数自然是当第一个参数为假时返回的表达式了。相当于我们的if cond then true_expr else false_expr:

let IfThenElse = lambda cond true_expr false_expr . cond true_expr false_expr

为了我们刚定义的布尔值有用,我们还得定义一些常用的逻辑操作先:

let BoolAnd = lambda x y . x y FALSE

let BoolOr = lambda x y. x TRUE y

let BoolNot = lambda x . x FALSE TRUE

上面定义了常用的“与”,“或”,和“非”操作。我们可以稍微考查一下它们的机制。

BoolAnd TRUE FALSE (也就是 true && false):

我们把TRUE和FALSE替换为它们的定义: BoolAnd (lambda x y . x) (lambda x y . y) 执行Alpha 替换避免混淆变量名:BoolAnd (lambda xt yt . xt) (lambda xf yf . yf) 然后把BoolAnd替换为它的定义:(lambda x y . x y FALSE)(lambda xt yt . xt) (lambda xf yf . yf)

执行Beta替换:(lambda xt yt . xt) (lambda xf yf . yf) FALSE

呵呵,再Beta一把:(lambda xf yf . yf)。

最后的结果lambda xf yf . yf就是FALSE的定义。也就是说, BoolAnd TRUE FALSE = FALSE。神奇吧?看起来只是简单的替换:变量替换,参数替换,但最后的结果确意义重大。这让我想起当年第一次读GEB时不由自主地感叹,看似简单的句法层面的操作竟然能得出迷幻般的结果。

我们再来看看 false && true, 也就是 BoolAnd FALSE TRUE。“噫,那不是和我们刚推演过的BoolAnd TRUE FALSE一样么!”。眼尖的老大们可能要问。嗯,我们知道布尔逻辑里的操作是服从交换率的,所以 a && b 等于 b && a。可惜我们在用lambda算子定义布尔操作,是不是服从交换率,需要我们证明。如果BoolAnd FALSE TRUE的结果是FALSE,我们也就证明了BoolAnd 符合交换率:

定义替换:BoolAnd (lambda x y . y) (lambda x y .x)

Alpha替换: BoolAnd (lambda xf yf . yf) (lambda xt yt . xt)

替换BoolAnd的定义: (lambda x y .x y FALSE) (lambda xf yf . yf) (lambda xt yt . xt) Beta替换: (lambda xf yf . yf) (lambda xt yt . xt) FALSE

再来Beta替换: lambda xt yt. xt, 也就是FALSE

所以说,BoolAnd FALSE TRUE = FALSE

最后,我们来看看BoolAnd TRUE TRUE:

定义替换:BoolAnd (lambda x y . x) (lambda x y . x)

Alpha替换: BoolAnd (lambda xa ya . xa) (lambda xb yb . xb)

替换BoolAnd的定义: (lambda x y . x y FALSE) (lambda xa ya . xa) (lambda xb yb . xb) Beta替换: (lambda xa ya . xa) (lambda xb yb . xb) FALSE

再次Beta替换: (lambda xb yb .xb),这个正是TRUE的定义

所以我们得到BoolAnd TRUE TRUE = TRUE

Lambda算子5a:Why oh why Y?

鄙视理论的老大们可以跳到下一篇文章,lambda算子5b。那里用JavaScript推导出Y。

原作在这里。我顺便参考了Richard Gabriel2001年写就的小文章The Why of Y。Gabriel 也是个牛人。在Lisp的发展史里举足轻重。写了无数重量级的LISP系统不说,还领导了Common LISP下面的标准OO系统,CLOS,的标准制订和实现。他提出的Lisp性能基准测试方法至今有用。G老大除了技术牛以外,还有很强的政治手腕。当年曾在山头林立的LISP组织间斡旋,促成了Common LISP标准的问世。不愧是当过公司老总的人。G老大的文章也写得相当有煽动力。他的Worse Is Better已经是网上引用率最高的文章之一。有兴趣的还可以去读他的自传。里面有趣的故事不少,有些还非常激动人心。比如当年他被一个心胸狭隘的小人老师诬陷,没有进入MIT 和哈佛,而后是怎么发奋的。现在G老大好像把兴趣转到写诗上面去了,从2000年开始就每日一诗。比赵丽华还勤奋。嗯,说远了。还是聊让人目眩的Y Combinator (组合子?)。

到上篇为止,我们已经一点一点把lambda算子建成一个有用的系统。我们搞定了数字,布尔值,和选择操作。唯一剩下的东西就是重复操作,也就是循环了。

靠,这个问题就要稍稍棘手一点了。Lambda算子的确用递归来实现循环。不过lambda算子里的

函数都没有名字,我们只好用一点小技巧来搞定佢。这个小技巧就是Y 组合子,也就是lambda的定点操作符。Y是Howard Curry在研究无类型的lambda算子时发现的。Y其实算最简单的组合子。复杂的还有着呢。可以想象我这种业余玩儿票的人学习编程语言理论时是多么痛苦。

先看一个非lambda算子的简单递归函数。嗯,猜对了,就是求阶乘,n!。没办法,标准例子嘛:factorial(n) = 1 if n = 0

factorial(n) = n * factorial(n-1) if n > 0

如果我们现在就开始写出lambda算子版本的阶乘函数的话,我们还得需要一点工具。。。我们需要测试一个值是否等于零,需要一个函数求积,还需要一个递减函数。

我们可以用一个名叫IsZero的函数来测是否和零相等。IsZero带三个参数,一个数,和两个值。如果这个数是0,IsZero返回第一个值,不然就返回第二个值。

至于乘法嘛。没有递归前怎么能写出乘法嗫?所以我们暂时假设我们用乘法函数,Mult x y。

而最后是我们的递减函数。我们用Pred x 来计算x – 1。

嗯,现在我们可以来搞定阶乘函数了。递归部分暂时空白:

lambda n . IsZero n 1 (Mult n (something (Pred n)))

注意哈。我们不得不用something暂时替代一下,因为我们没有任何函数名可用。不像上面的factorial(n) = n * factorial(n-1) if n > 0,要递归的时候调用同一个函数名,传入不同的参数就行了。所有现在问题就是:我们怎么才能让这个something 递归起来?

这个问题的答案就是所谓的组合子了。组合子是特殊的高阶函数(高阶函数的参数是函数,返回值也是函数)。定义这个函数时除了应用函数,不需要引用其它任何东西。Y组合子好像有种魔力,能让递归变得可能。它的定义如下:

let Y = lambda y . (lambda x . y (x x)) (lambda x . y (x x))

仔细观察一下这个定义,可以发现我们称这个函数为Y的原因在于它的形状。我们把上面的公式写成解析树的形式。把你的显示器倒过来,就可以看到一个接一个地Y了。^_^

Y组合子特别的地方在于把Y应用到Y身上后返回的是对这个应用自身的应用。也就是说,(Y Y) = Y (Y Y). 我们来推导一下(Y Y)

1.Y Y

2.展开第一个Y:

(lambda y . (lambda x . y (x x)) (lambda x . y (x x))) Y

3.来一把beta:

(lambda x . Y (x x)) (lambda x. Y (x x))

4.第二个lambda式子里有x, 和第一个式子里的x冲突了,所以来个Alpha[x/z] :把第二

个lmabda式子里的x换成z:

(lambda x . Y (x x)) (lambda z. Y (z z))

5.再来Beta:

Y ((lambda z. Y (z z)) (lambda z. Y (z z)))

6.把Y展开,然后对y和x做alpha变换:alpha[y/a][x/b]:

(lambda a . (lambda b . a (b b)) (lambda b . a (b b))) ((lambda z. Y (z z)) (lambda z. Y (z z)))

7.再Beta,不要嫌长哈:

(lambda b . ((lambda z. Y (z z)) (lambda z. Y (z z))) (b b)) (lambda b . ((lambda z. Y (z z)) (lambda z. Y (z z))) (b b))

现在仔细观察这个表达式。正是Y (Y Y)!要记得(Y Y) 恰好等于上面第3条的结果,(lambda x . Y (x x)) (lambda x . Y (x x))。再结合Y的定义,自然得到Y Y = Y (Y Y)。这也是Y的魔力:通过把自身应用到自身,它再造了自己:(Y Y) = Y (Y Y) = Y (Y (Y Y)), 子子孙孙,无穷无尽。Y最重要的特性便是Y F = F (Y F)。Y一附身,就有定点了。神奇得紧啊。

那我们怎么运用这个疯狂的东东嗫?

嗯,我们做一点点尝试先。先用一个名字(就像写普通的递归函数一样)试试:

let fact = lambda n . IsZero n 1 (Mult n (fact (Pred n)))

现在的问题是,‖fact‖并不是一个在fact内定义好的标识符。我们怎么才能让lambda式子内的‖fact‖指向‖fact‖的函数定义?思考ing….嗯,我们可以再做一个lmabda函数,以便我们把‖fact‖函数当成参数传进去,然后如果我们可以想法写出一个能把自己当成参数传给自己的‖fact‖, 就大功告成了。我们把这种自己玩儿自己的函数叫做metafact:

let metafact = lambda fact . (lambda n . IsZero n 1 (Mult n (fact (Pred n))))

注意哈,(metafact fact) n = fact n。也就是说,fact是metafact这个函数的一个定点。定点的定义其实很简单:F(X) = X的话,X就是函数F的一个定点。当年高一时好像就学了这个概念。想不到的是这个看似简单的概念竟然在许多计算机科学理论和应用里扮演重要角色。比如程序分析,比如格点理论,比如模型检验,比如μ-算子,比如拓扑。

现在我们再把metafact应用到它自身,就得到了阶乘函数:

fact n = (metafact metafact) n.

这里揭示了一个非常重要的技巧:我们实现函数自我引用(self-reference)的基本方法就是把一个函数应用到他自身。而通过自我引用完成递归就是Y起作用的地方。它让我们构建一个怪异的结构。在这种结构里,上面式子里的函数可以被复制到我们需要递归的地方。如下式子可以实现我们的构想:metafact (Y metafact)。把它展开:

(lambda fact . (lambda n . IsZero n 1 (Mult n (fact (Pred n))))) (Y (lambda fact . (lambda n . IsZero n 1 (Mult n (fact (Pred n))))))

(Y metafact)就是lambda函数metafact参数fact的值。当我们对该函数应用β转换时,如果n 为0,函数返回1。如果n不为0,我们则调用Mult n (fact (Pred n))。对Fact应用beta转换, 我们得到Y metafact (就是我们传进去的参数哈)。结合Y的疯狂魔术,我们得到metafact (Y metafact) (Pred n)。

哈哈!递归。变态之极的递归。

原文作者在大学里学到Y组合子时—应该是1989年左右—直到现在作者还觉得Y神秘。虽然作者现在理解Y了,但还是很难想象怎么有人能想出Y这个东西!

如果你对Y有兴趣,我们强烈推荐The Little Schemer这本书。该书非常精彩,写得象一本儿童读物。书里每一页正面是一个问题,背面就是答案。书的风格轻松幽默,引人入胜,而且它不仅是教你Scheme编程而已。

其实Y组合子有好几个版本。计算lambda表达式的方式有好几种。给出一个如下表达式:(lambda x y . x * y) 3 ((lambda z. z * z) 4)

我们可以采用不同的顺序来计算:先对(lambda x y . x * y )做beta变换,得到

3 * ((lambda z . z * z) 4)

或者,我们可以对((lambda z . z * z) 4)做beta变换先:

(lambda x y . x * y) 3 (4 * 4)

这个例子里,两种不同的方法得出同样的结果。但对有些表达式却不然。第一种顺序叫做lazy evaluation:只有在需要一个函数时我们才计算它。第二种顺序叫做eager evaluation:我们总是计算出一个参数的值再把该参数传给一个函数。编程语言里面,Lisp, Scheme, 和ML这三种基于lambda算子的语言采用eager evaluation。Haskell和Mrianda这两种基于lambda算子的语言采用lazy evaluation。这篇帖子里的Y 组合子是对lazy evaluation的Y。如果我们采用eager evaluation,前述的Y组合子就失效了――它会不停地拷贝Y,形成死循环。

Lambda算子5b:How of Y

其实是这篇文章的意译。有些东西省了。添了点私货。就有了下面的帖子。

虽然Y相当神奇。对它的推导也不完全是天外飞仙般无迹可寻。基本上我们为了解决让没有名字的函数能自我引用,一步一步抽象出了Y。所以知道Y的推导过程对我们程序员还是很有意义的:毕竟编程的过程也是抽象的过程。看看当年的老大们怎么从纷繁的表象里抽象出一般规律,对我们日后的思考应该大有好处。为了老大们能够试验,我就用JavaScript了。整个推导的过程非常像编程时的重构。我们提出一个粗略的解决方案,然后仔细观察,找出可以抽象的地方,进行抽象。得到一个更普适的结果后,继续重复重构的步骤,直到得到最优解。估计看这篇帖子的人都知道怎么玩儿小小JavaScript吧?再说有个浏览器就有了测试环境。废话少说,看代码。我们还是以阶乘函数为例。先看通常的写法:

1: function fact(n){

2: if(n ==0){

3: return1;

4: }

5:

6: if(n >0){

7: return n *fact(n -1);

8: }

9: }

上面的JavaScript函数定义内部调用自己。这种调用可行的前提是我们用函数名指代函数定义。也就是说,fact这个名字绑定的函数定义就是上面的函数体。如果我们不能通过名字来调用函数怎么办呢(就跟lambda算子一样)?也许有老大会问:为什么增加这个限制呢?不是自虐么?理由很简单:理论需要探求事物本质。记得奥卡姆剃刀吧?如无必要,毋增实体。函数名到底是必需元素,还是句法糖?这种研究方法也有实际的意义:再复杂的系统也是在简单但完备的基础上搭建起来的。强大的编程工具,总是基于于层层叠加的抽象,而最低级的抽象层总是非常简单。简单意味着透彻,简

单意味着健壮。简单意味着灵活。简单意味着经济。问题是,到底简单到什么地步?怎么保证系统不至于简单到一无所用的地步?这和逻辑学家建立系统时总是要证明系统的正确性和完备性一个道理。而找到了Y,我们也就明白了,原来函数名绑定并非本质。

嗯,继续。函数fact是递归的基本形式。既然我们不能直接在函数体内通过函数名调用另一个函数,我们至少可以把想调用的函数通过参数传进去。于是我们得到fact2:

09: fact2 =function(himself, n){

10: return function(n){

11: if(n <2){

12: return1;

13: }

14:

15: return n *(something_expression_with_himself);

16: }

17: }

我用JavaScript里的匿名函数来强调lambda函数不具名的特征。这里用fact2纯粹为了方便。变量fact2本身并没有参与运算。如果我们这样调用:fact2(fact2, 3),那fact2这个函数不就可以调用自身了么?这也就是前面提到的自我引用的技巧:加一层抽象,从而把自己通过参数传给自己。现在我们要解决的就是,到底something_expression_with_himeself是什么东西。因为fact2和himself都必须递归调用自己,something_expression_with_himself从直觉上应该是himself(himself,n-1)开始。看到没有?函数体和调用方式不变,但n变成n-1了。和递归一致了哈:

19: fact3 =function(himself, n){

20: if( n <2){

21: return1;

22: }

23:

24: return n *himself(himself, n-1);

25: }

在你的JavaScript console里试验一下:fact3(fact3, 3) 的确返回6。什么,没有JavaScript console? 老大啊,上网的银能不用FireFox么?用了FireFox的程序员能不用FireBug么?快去下载吧。

下载完了?那我们继续。记得lambda算子里的函数只接受一个参数吧?可是fact3接受两个函数。所以我们要撒一点―咖喱‖:

17: fact4 =function(himself){

18: return function(n){

19: if( n <2){

20: return1;

21: }

22:

23: return n *himself(himself)(n-1);

24: }

25: }

运行一下三。看见才能相信哈。fact4(fact4)(3) = 6, fact4(fact4)(4) = 24, fact4(fact4)(5)=120。。。。

现在的问题是,我们要把这个具体例子的递归方法抽象出来。现在我们需要把和自我引用无关的细节同自我引用本身分开。因为我们关心的是怎么运用自我引用来解决递归问题。我们可以先解决分离himself和n有关的代码。这样做是因为管理n的代码只与求阶乘有关,不是我们关心的重点。好比分离框架逻辑和业务逻辑。于是我们得到函数fact5。

37: fact5 =function(h){

38: return function(n){

39: var f =function(q){

40: return function(n){

41: if(n <2){

42: return1;

43: }

44:

45: return n *q(n-1);

46: }

47: }

48:

49: return f(h(h))(n);

50: }

51: }

运行几个例子增强点信心哈:fact5(fact5)(3) = 6, fact5(fact5)(4)=24。。。

注意函数f其实不用嵌在fact5里面。所以我们可以写成:

37: fucntion f(q){

38: return function(n){

39: if(n <2){

40: return1;

41: }

42:

43: return n *q(n-1);

44: }

45: }

46:

47: function fact5(h){

48: return function(n){

49: return f(h(h))(n);

50: }

51: }

现在就可以看出两件事了:一是新的函数f不过是参数化的阶乘函数―― 递归部分变成参数(也是

一个函数)q了。第二,我们可以把f进一步抽象出来,剥离具体的阶乘部分,于是得到了Y:69: function Y(f){

70: g =function(h){

71: return function(x){

72: return f(h(h))(x);

73: }

74: }

75:

76: return g(g);

77: }

现在我们可以用Y来实现阶乘了:

79: fact6 =Y(

80: function(h){

81: return function(n){

82: if(n <2){

83: return1;

84: }

85:

86: return n *h(n-1);

87: }

88: }

89: );

试一下,比如fact6(3) = 6, fact6(4) = 24。。。。

长出一口气。连写了4个小时,终于把关于Y的东西写完了。下面可以谈更宽泛的组合算子了。主要是S,K,和I三个算子。有个变态编程语言unlambda就是靠解析SKI为生的。

排列组合c怎么算 λ-演算与组合算符初步介绍

J. Roger Hindley Lambda?Calculus and Combinators An Introduction 2008; Hardback ISBN9780521898850 J.R.欣德利等著 λ-演算和组合逻辑是逻辑的两个系统,它们都发挥了抽象编程语言的作用。这两者都旨在描述程序的极为通用的性质。在某些方面,它们是互相竞争的,在其他它们又是相互支撑的。λ-演算是美国逻辑学家A.Church在1930年左右发明的,它是作为包括高阶算子(即可以作用于其他算子的算子)在内的概括逻辑系统的一部分。事实上λ-演算语言或某些本质上等价的表示法,是大多数高阶语言的关键部分,无论这种语言是逻辑的,还是计算机编程的。本书的目的就是向读者介绍这两个领域的基本方法与结果。作者并不要求读者具有这两个领域的初步知识,但是要求读者具有一些有关命题逻辑、谓词逻辑和递归函数的知识,并且具有某些数学归纳法的经验。 本书共有16章。λ-演算;组合逻辑;λ的幂与组合算符;可计算函数的表示;不可判定性理论;形式理论λ-β与CLw;λ?演算中的外延;组合逻辑中的外延性;λ与组合逻辑之间的对应;10.简单类型化Church式样;1简单类型化组合逻辑

的Curry式样;1简单类型化λ中的Curry式样;1类型化推广;1组合逻辑模型;1λ-演算模型;1Scott的D∞与其他模型。最后是5个附录。 本书值得向任何想要研究组合逻辑与λ-演算的逻辑学家及计算机科学家郑重推荐。 胡光华, 高级软件工程师 (原中国科学院物理学研究所) Hu Guanghua, Senior Software Engineer (Former Institute of Physics,CAS)

实验1图灵机模型与计算机硬件系统虚拟拆装实验报告

实验1 图灵机模型与计算机硬件系统虚拟拆装实验报告 学号51 姓名叶思凡班级:卫生检验与检疫15 实验时间: 2017年 2月 23 日

在本次实验中,你有哪些收获?遇到哪些问题?这些问题是否已经解决?如果已经解决了,请说说你是如何解决的。也可谈谈你的其它想法。 在本次实验中,我认识到图灵机模型组成和冯诺依曼计算机体系组成及其功

能,并且了解到最初的计算机是如何诞生并运行的。在实验中,对于图灵机模型模拟过程,以及冯诺依曼计算机的运行难以理解。在搜素相关资料并询问老师后,得知图灵机是为了用机器模拟人的运算过程而实现的,图灵机是通过纸带来读取一个空格的信息,并根据控制器当前的状态和控制规则,改变控制器当前的状态,而冯诺依曼计算机结构则是通过计算机硬件设备将许多命令按一定的顺序组成的程序,然后把程序和数据一起输入计算机,计算机对已存入的程序和数据处理后,输出结果。 第一周作业题:(请认真查阅教材及相关资料,回答以下问题,并把答案附在问题之后)1.什么是图灵机的理论模型?其核心思想与贡献是什么? 答:图灵机模型是指图灵机具有一个有穷控制器, 一条两端无穷的输入输出带和一个带头,带划分为单元格, 每个单元格可以放置一个符号, 带头每次根据当前状态和带头处单元格的符号内容, 根据转移规则选择下一个动作, 每个动作都包括下一个状态, 修改带头处单元格的符号以及带头向左或向右移动一个单元。 图灵机的思想是关于数据、指令、程序及程序/指令自动执行的基本思想。 其贡献主要有:1、图灵机模型理论是计算学科最核心的理论之一;2、图灵机模型为计算机设计指明了方向;3、图灵机模型是算法分析和程序语言设计的基础理论。 2.什么是冯.诺依曼计算机体系结构?为什么说它是现代计算机的基础? 答:冯诺依曼的计算机体系结构是:数学计算机的数制采用二进制;计算机应该按照程序顺序执行。人们把冯·诺依曼的这个理论称为冯·诺依曼体系结构 从ENIAC到当前最先进的计算机都采用的是冯·诺依曼体系结构。所以冯·诺依曼是当之无愧的数字计算机之父。冯诺依曼计算机体系机构也是现代计算机的基础。

Lambda表达式详细总结

(一)输入参数 在Lambda表达式中,输入参数是Lambda运算符的左边部分。它包含参数的数量可以为0、1或者多个。只有当输入参数为1时,Lambda表达式左边的一对小括弧才可以省略。输入参数的数量大于或者等于2时,Lambda表达式左边的一对小括弧中的多个参数质检使用逗号(,)分割。 示例1 下面创建一个Lambda表达式,它的输入参数的数量为0.该表达式将显示“This is a Lambda expression”字符串。 [csharp]view plain copy 1.()=>Console.WriteLine("This is a Lambda expression."); 分析2 由于上述Lambda表达式的输入参数的数量为0,因此,该Lambda表达式的左边部分的一对小括弧不能被省略。 示例2 下面创建一个Lambda表达式,它的输入参数包含一个参数:m。该表达式将计算m参数与2的乘积。 [csharp]view plain copy 1.m=>m*2; 分析2 上述Lambda表达式的输入参数省略了一对小括弧,它与“(m)=>m*2”Lambda表达式是等效的。 示例3 下面创建一个Lambda表达式,它的输入参数包含两个参数:m和n。该表达式将计算m和n 参数的乘积。 [csharp]view plain copy 1.(m,n)=>m*n;

(二)表达式或语句块 多个Lambda表达式可以构成Lambda语句块。语句块可以放到运算符的右边,作为Lambda 的主体。根据主题不同,Lambda表达式可以分为表达式Lambda和语句Lambda。语句块中可以包含多条语句,并且可以包含循环、方法调用和if语句等。 示例1 下面创建一个Lambda表达式,它的右边部分是一个表达式。该表达式计算m参数的平方值。[csharp]view plain copy 1.m=>m*n; 分析1 如果Lambda表达式的右边部分是一个语句块,那么该语句块必须被"{"和"}"包围。 示例2 下面创建一个Lambda表达式,它的输入参数包括两个参数:m和n。该表达式的右边包含2个表达式;第一个表达式计算m和n参数的乘积,结果保存为result变量;第二个表达式显示result变量的值。 [csharp]view plain copy 1.(m,n)=>{int result=m*n; Console.WriteLine(result);} 分析2 上述Lambda表达式的右边部分包含2个表达式,因此,该表达式的右边部分必须被"{"和"}"包围。 (三)查询表达式 查询表达式是一种使用查询语法表示的表达式,它用于查询和转换来自任意支持LINQ的数据源中的数据。查询表达式使用许多常见的C#语言构造,易读简洁,容易掌握。它由一组类似于SQL或XQuery的声明性语法编写的子句组成。每一个子句可以包含一个或多个C#表达式。这些C#表达式本身也可能是查询表达式或包含查询表达式。 ●查询表达式必须以from子句开头,以select或group子句结束。第一个from子句和最后一个select子句或group子句之间,可以包含一个活多个where子句、let子句、join 子句、orderby子句和group子句,甚至还可以是from子句。它包括8个基本子句,具体说明如下所示。 ●from子句:指定查询操作的数据源和范围变量。 ●select子句:指定查询结果的类型和表现形式。 ●where子句:指定筛选元素的逻辑条件。 ●let子句:引入用来临时保存查询表达式中的字表达式结果的范围变量。

计算程序_计算流体力学_对流方程_有限差分法_Lax格式_迎风格式_FTCS格式

% 一维对流方程迎风格式、Lax格式、FTCS格式差分法计算 % 潭花林清华大学航天航空学院 % FTCS格式对于一维对流方程不稳定,最好不用 clc clear all % 1.参数定义 dx=1; x1=-18; x2=18; x=x1:dx:x2; L1=length(x); % dt=0.5*dx; % 收敛 dt=2*dx; % 不收敛 t1=0; t2=t1+80*dt; t=t1:dt:t2; L2=length(t); alpha=1; lambda=alpha*dt/dx; geshi=1; % 迎风格式 % geshi=2; % Lax格式 % geshi=3; % FTCS格式 % 2.显式求解 zeta=zeros(L1,L2);

for kk=1:3 geshi=kk; for ii=1:L1 if x(ii)>0 zeta(ii,1)=1; else if x(ii)==0 zeta(ii,1)=1/2; else if x(ii)<0 zeta(ii,1)=0; end end end end if geshi==1 for ii=2:L1 for jj=1:(L2-1) zeta(ii,jj+1)=zeta(ii,jj)-lambda*(zeta(ii,jj)-z eta(ii-1,jj)); end zeta(1,jj+1)=zeta(2,jj+1); end zeta1=zeta;

else if geshi==2 for ii=2:(L1-1) for jj=1:(L2-1) zeta(ii,jj+1)=(zeta(ii+1,jj)+zeta(ii-1,jj))/2-. .. lambda/2*(zeta(ii+1,jj)-zeta(ii-1,jj)); end zeta(1,jj+1)=zeta(2,jj+1); zeta(L1,jj+1)=zeta(L1,jj)-lambda*(zeta(L1,jj)-z eta(L1-1,jj)); end zeta2=zeta; else if geshi==3 for ii=2:(L1-1) for jj=1:(L2-1) zeta(ii,jj+1)=zeta(ii,jj)-lambda/2*(zeta(ii+1,j j)-zeta(ii-1,jj)); end zeta(1,jj+1)=zeta(2,jj+1); zeta(L1,jj+1)=zeta(L1,jj)-lambda*(zeta(L1,jj)-z eta(L1-1,jj));

物联网的四种计算模式

物联网的四种计算模式

目录 1. 物联网的云计算 (4) 2. 面向物联网的雾计算 (5) 3. 物联网边缘计算 (6) 4. 物联网的MIST 计算 (7)

从物联网从业者的角度来看,经常看到对计算更加可用和分布式的需求。当开始将物联网与OT 和IT系统整合时,面临的第一个问题是设备发送到服务器的庞大数据量。在一个工厂自动化的场景中,可能有数百个集成的传感器,这些传感器每1秒发送3个数据点。大部分的传感器数据在5秒钟之后就完全没用了。数百个传感器,多个网关,多个进程,和多个系统,需要几乎在瞬间处理这些数据。 大多数数据处理的支持者都支持云模型,即总是应该向云发送一些东西。这也是第一种物联网计算基础。

通过物联网和云计算模型,基本上推动和处理你的感官数据在云。你有一个摄入模块,它可以接收数据并存储在一个数据湖(一个非常大的存储器) ,然后对它进行并行处理(它可以是Spark,Azure HD Insight,Hive,等等) ,然后使用快节奏的信息来做决定。 自从开始构建物联网解决方案,现在有了许多新的产品和服务,可以非常容易地做到这一点: ?可以使用AWS Kinesis 和Big data lambda services ?可以利用Azure 的生态系统,让构建大数据能力变得极其容易 ?或者,可以使用像Google Cloud 产品这样的工具如Cloud IoT Core 在物联网中面临的一些挑战是: ?私有平台的使用者和企业对于拥有他们的数据在谷歌,微软,亚马逊等感到不舒服 ?延迟和网络中断问题 ?增加了存储成本、数据安全性和持久性

C#之lambda表达式

C#的Lambda 表达式都使用 Lambda 运算符 =>,该运算符读为“goes to”。语法如下: 形参列表=>函数体 函数体多于一条语句的可用大括号括起。 类型 可以将此表达式分配给委托类型,如下所示: ? 1 2 3 delegate int del(int i); del myDelegate = x => { return x * x; }; int j = myDelegate(5); //j = 25 创建表达式目录树类型: ? 1 2 3 using System.Linq.Expressions; // ... Expression = x => x * x; => 运算符具有与赋值运算符 (=) 相同的优先级,并且是右结合运算符。 Lambda 用在基于方法的 LINQ 查询中,作为诸如 Where 和 Where 等标准查询运算符方法的参数。 使用基于方法的语法在 Enumerable 类中调用 Where 方法时(像在 LINQ to Objects 和 LINQ to XML 中那样),参数是委托类型 System..::.Func<(Of <(T, TResult>)>)。使用 Lambda 表达式创建委托最为方便。例如,当您在 System.Linq..::.Queryable 类中调用相同的方法时(像在 LINQ to SQL 中那样),则参数类型是 System.Linq.Expressions..::.Expression,其中 Func 是包含至多五个输入参数的任何 Func 委托。同样,Lambda 表达式只是一种用于构造表达式目录树的非常简练的方式。尽管事实上通过 Lambda 创建的对象的类型是不同的,但 Lambda 使得 Where 调用看起来类似。 在前面的示例中,请注意委托签名具有一个 int 类型的隐式类型输入参数,并返回 int 。可以将 Lambda 表达式转换为该类型的委托,因为该表达式也具有一个输入参数 (x),以及一个编译器可隐式转换为 int 类型的返回值。(以下几节中将对类型推理进行详细讨论。)使用输入参数 5 调用委托时,它将返回结果 25。 在 is 或 as 运算符的左侧不允许使用 Lambda 。 适用于匿名方法的所有限制也适用于 Lambda 表达式。有关更多信息,请参见匿名方法(C# 编程指南)。 特殊 下列规则适用于 Lambda 表达式中的变量范围:

python语言零基础入门-函数

1、函数的基本概念及调用(ppt) 2、自定义函数 如何创建函数?def语句 In [2]: # 定义函数 def f(x): if x <5: print('输入值小于5') else: print('输入值大于等于5') # 定义函数,其中x是参数(局部变量)f(10) # 运行函数 输入值大于等于5 In [3]: # 关于retuen def f1(x): y =2**x # 没有return def f2(x): y =2**x return y # 含有return print(f1(2),f2(2)) # return语句退出函数,并返回一个表达式。不带参数值的return语句返回None None 4 In [4]: # 默认参数 def f(x,n =2): return(x**n) print(f(10)) print(f(10,3)) # n = 2,这里n的默认值为2,如果不输入则以默认值为主 100 1000

小作业 ① 函数f (x ),输入一个字符串,分别print 出每个字母 ② f(x,y,z),函数内部算法:生成 ((x+y)*(x-y))**z ③ 编写一个求平均值的函数 f(*m) ④ 定义一个函数,用于求矩形面积、圆形面积 ⑤ 定义一个函数,函数的作用是把输入的列表变成一连串字典的key ,并生成字典,需要用input 输入 3、局部变量及全局变量 定义在函数内部的变量拥有一个局部作用域,定义在函数外的拥有全局作用域。 局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。调用函数时,所有在函数内声明的变量名称都将被加入到作用域中 In [6]: (1,) ('a', 'b') (1, 2, 3, [44, 33]) ('a', 'b') 请输入一个数字:60 函数内为局部变量:呵呵哒 函数外为全局变量:60 # 可变参数 def f (*x): print (x) return x f(1) f('a','b') f(1,2,3,[44,33]) print (type (f('a','b'))) # 通过*来定义可变参数# 默认会把可变参数传入一个元祖! # 演示案例 def f (m): m = '呵呵哒' # 函数作用:把输入变量指向“呵呵哒” print ("函数内为局部变量:%s" % m) a = input ('请输入一个数字:') f(a) print ("函数外为全局变量:%s" % a) # f (m )中,m 是函数的参数,f(x)是吧x 的值赋予了m ,但x 值自己本身不受影响,所以执行函数后,是在函数局部# 什么是局部变量? → 当函数定义内声明变量的时候,它们与函数外具有相同名称的其他变量没有任何关系!# 即变量名称对于函数来说是“局部”的。这称为变量的作用域。所有变量的作用域是它们被定义的块,从它们的

MATLAB概率习题

数学实验(概率论)题目一.用MATLAB 计算随机变量的分布 1.用MATLAB 计算二项分布 在一级品率为0.2的大批产品中,随机地抽取20个产品,求其中有2个一级品的概率。 1. 用MATLAB 计算泊松分布 用MATLAB 计算:保险公司售出某种寿险保单2500份.已知此项寿险每单需交保费120元,当被保人一年内死亡时,其家属可以从保险公司获得2万元的赔偿(即保额为2万元).若此类被保人一年内死亡的概率0.002,试求: (1)保险公司的此项寿险亏损的概率; (2)保险公司从此项寿险获利不少于10万元的概率; (3)获利不少于20万元的概率. 3.用MATLAB 计算均匀分布 乘客到车站候车时间ξ ()0,6U ,计算()13P ξ<≤。 4.用MATLAB 计算指数分布 用MATLAB 计算:某元件寿命ξ服从参数为λ(λ=1 1000-)的指数分布.3个这样的元件使用1000小时后,都没有损坏的概率是多少? 5。用MATLAB 计算正态分布 某厂生产一种设备,其平均寿命为10年,标准差为2年.如该设备的寿命服从正态分布,求寿命不低于9年的设备占整批设备的比例? 二.用MATLAB 计算随机变量的期望和方差 1.用MATLAB 计算数学期望 (1)用MATLAB 计算离散型随机变量的期望 1)。一批产品中有一、二、三等品、等外品及废品5种,相应的概率分别为0.7、0.1、

0.1、0.06及0.04,若其产值分别为6元、5.4元、5元、4元及0元.求产值的平均值 2)。已知随机变量X 的分布列如下:{}k k X p 2 1 == ,,2,1n k =计算.EX (2)用MATLAB 计算连续型随机变量的数学期望 假定国际市场上对我国某种商品的年需求量是一个随机变量ξ(单位:吨),服从区间[],a b 上的均匀分布,其概率密度为: 1()0 a x b x b a ??≤≤?=-???其它 计算我国该种商品在国际市场上年销售量的期望.ξE . (3)用MATLAB 计算随机变量函数的数学期望 假定国际市场每年对我国某种商品的需求量是随机变量X (单位:吨),服从[20,40]上的均匀分布,已知该商品每售出1吨,可获利3万美元,若销售不出去,则每吨要损失1万美元,如何组织货源,才可使收益最大? 2. 用MATLAB 计算方差 (1)利用MATLAB 计算:设有甲、乙两种股票,今年的价格都是10元,一年后它们的价格及其分布分别如下表: 试比较购买这两种股票时的投资风险.。 (2)计算:1(2)中我国商品在国际市场上的销售量的方差.。 3. 常见分布的期望与方差

图灵与图灵机

图灵与图灵机 关于图灵的介绍: 图灵是著名的数学家,逻辑学家,是计算机和人工智能之父。 图灵对于人工智能的发展有诸多贡献,提出了一种用于判定机器是否具有智能的试验方法,即图灵测试,至今,每年都有试验的比赛。此外,图灵提出的著名的图灵机模型为现代计算机的逻辑工作方式奠定了基础。 关于图灵机的介绍: 根据了解,图灵机是一种抽象的机器(没有实体机),是一种任意解决数学逻辑过程的机器,是一种理论上的通用机(在50年代计算机只能解决某一特定逻辑问题)。 图灵机是模拟人写字的过程,包括两个步骤:1.在纸上写入或擦去一个符号;2.把注意力从纸的一个位置移动到另一个位置。把注意力从纸的一个位置移动到另一个位置。 其包括了以下几个部件: 1.读写头,它可以读出和改变纸上的符号,并且可以左右移动; 2.状态寄存器,用于保存图灵机所处在的状态(包括停机问题); 3.控制规则,根据读写头的状态和纸带上的字符来确定下一步动作,并改变状态寄存 器的值; 4.无限长的纸带,字母符号记录的载体; 这个机器可以解决人类已知的所有计算问题,以及由其衍生的停机问题对数学和计算机的发展产生重大影响。 下面我来讲讲停机问题: 其本质问题是: 给定一个图灵机T,和一个任意语言集合S,是否T会最终停机于每一个s∈S。其意义相同于可确定语言。显然任意有限个S是可判定性的,可列的S也是可停机的。 通俗的说,停机问题就是判断任意一个程序是否会在有限的时间之内结束运行的问题。如果这个问题可以在有限的时间之内解决,则有一个程序判断其本身是否会停机并做出相反的行为,这时候显然不管停机问题的结果是什么都不会符合要求。所以这是一个不可解的问题。 这和理发师的问题有着很大的相似性,停机问题是目前逻辑学的焦点,和第三次数学危机的解决方案。 图灵机还有许多变种: 多带图灵机,非确定性图灵机,枚举器等(来自百度,对此不太了解)

Lambda计算

Lambda计算——Brettschneider方程式、一般原理及方法 Brettschneider方程式是实际上的标准方法,用于计算标准化的空气/燃油平衡(Lambda),适用于国内和国际I&M检查程序。它源于Johannes Brettschneider博士1979在Robert Bosch 的论文,发表在“Bosch technische Berichte”6(1979)卷4册177-186页。在该论文中,Brettschneider博士制定了通过在排放时,比较氧分子和碳、氢分子的比例来计算Lambda(空气/燃油平衡)的方法。这个方程式有点复杂,但从测量的CO、CO2的值,未燃烧的HC、和未耗尽的O2,相对便于计算。 式中: [XX]=气体浓度,% H CV=燃油中氢/碳原子比 O CV=燃油中氧/碳原子比 Cfactor=测量时,每个HC分子中碳原子量 (Cfactor是燃油-特定值。己烷=6,丙烷=3,甲烷=1) 以上的方程式好比所有的氧是分子,全部碳、氢源是分母(水浓度由CO2和CO之和,及CO/CO2的比乘“3.5”项的分数来确定)。Brettschneider方程式的结果是Lambda(λ)项,一个无量纲项,它与空气/燃油的化学计算值密切相关。在化学计算点上,Lambd a=1.000。 1.050的Lambda值是5.0%稀;0.950的Lambda值是5.0%浓。如果计算Lambd a,可很方便确定A/F比,即简单地用Lambda乘选用燃料的A/F比——也就是:汽油-14.71、液化石油气-15.87、天然气-17.45。 Brettschneider方程式说明 虽然从理论上了解本方程式可能有写困难,但在实际运用中却简单。该方程式直接表现空气/燃油混合物的“稀的程度”,——很大程度地如何使燃油氧化——也是构成空气/燃油平衡值得考虑的重要因素。虽然使用这个方程式完全是运用功能,然而它就是对传统管理的极好的更换,例如:浓应用(性能调整)的CO测量、“大范围的lambda传感器”(这些传感器不只是非线性的,也对排放气的易燃以及EGT(系火焰的温度和体积)非常灵敏。 迄今为止,我们只发现稳定的空气/燃油比的测量,它是:首先构成排放气流(至少HC、CO、CO2和O2四种气)中组成气的精确测量,计算氧和易燃物成分,而后是lambda和A/F值。 Lambda和A/F比的关系 当氧和易燃物处于完美的化学计算平衡时,因为Lambda=1.000,Lambda很便于计算实际使用燃油的A/F比。 活动的A/F比就是Lambda乘特定燃油的化学计算A/F比(汽油为14.71,而其它燃油有不同的值——见下)。Brettschneider方法使用所有的氧、含碳的气计算空气/燃油比,此方法远

物联网的四大计算基础解析

物联网的四大计算基础解析 从物联网从业者的角度来看,经常看到对计算更加可用和分布式的需求。当开始将物联网与OT和IT系统整合时,面临的第一个问题是设备发送到服务器的庞大数据量。在一个工厂自动化的场景中,可能有数百个集成的传感器,这些传感器每1秒发送3个数据点。大部分的传感器数据在5秒钟之后就完全没用了。数百个传感器,多个网关,多个进程,和多个系统,需要几乎在瞬间处理这些数据。 大多数数据处理的支持者都支持云模型,即总是应该向云发送一些东西。这也是第一种物联网计算基础。下面就随着物联网解决方案供应商云里物里科技一起来看下这四大基础计算的详细介绍。 1.物联网的云计算 通过物联网和云计算模型,基本上推动和处理你的感官数据在云。你有一个摄入模块,它可以接收数据并存储在一个数据湖(一个非常大的存储器),然后对它进行并行处理(它可以是Spark,Azure HD Insight,Hive,等等),然后使用快节奏的信息来做决定。 自从开始构建物联网解决方案,现在有了许多新的产品和服务,可以非常容易地做到这一点: 可以使用AWS Kinesis和Big data lambda services

可以利用Azure的生态系统,让构建大数据能力变得极其容易 或者,可以使用像Google Cloud产品这样的工具如Cloud IoT Core 在物联网中面临的一些挑战是: 私有平台的使用者和企业对于拥有他们的数据在谷歌,微软,亚马逊等感到不舒服 延迟和网络中断问题 增加了存储成本、数据安全性和持久性 通常,大数据框架不足以创建一个能够满足数据需求的大型摄入模块 2.面向物联网的雾计算 通过雾计算,可以变得更加强大。雾计算使用的是本地处理单元或计算机,而不是将数据一路发送到云端并等待服务器处理和响应。 4-5年前,还没有像Sigfox和LoraWAN那样的无线解决方案,BLE也没有mesh 或远程功能。因此,必须使用更昂贵的网络解决方案,以确保能够建立一个安全,持久的连接到数据处理单元。这个中心单元是解决方案的核心,很少有专业的解决方案提供商。 从实施一个雾网络中可以了解到: 这并不是很简单,需要知道和理解很多事情。构建软件,或者说在物联网上所做的,是更直接和开放的。而且,当把网络当成一道屏障时,它会降低速度。 对于这样的实现,需要一个非常大的团队和多个供应商。通常也会面临供应商的锁定。 OpenFog是一个由著名业内人士开发的专为雾计算架构而设计的开放雾计算框架。它提供了用例,试验台,技术规格,还有一个参考体系结构。 3.物联网边缘计算 物联网是关于捕捉微小的交互作用,并尽可能快地做出反应。边缘计算离数据源最近,能够在传感器区域应用机器学习。如果陷入了边缘和雾计算的讨论,应该明白,边缘计算是所有关于智能传感器节点的应用,而雾计算仍然是关于局域网络,可以为数据量大的操作提供计算能力。

Lambda表达式详细总结

lambda简介 lambda运算符:所有的lambda表达式都是用新的lambda运算符 " => ",可以叫他,“转到”或者“成为”。运算符将表达式分为两部分,左边指定输入参数,右边是lambda的主体。 lambda表达式: 1.一个参数:param=>expr 2.多个参数:(param-list)=>expr https://www.wendangku.net/doc/7513043544.html,/wangboxian/article/details/41963205 Lambda表达式详细总结 (一)输入参数 在Lambda表达式中,输入参数是Lambda运算符的左边部分。它包含参数的数量可以为0、1或者多个。只有当输入参数为1时,Lambda表达式左边的一对小括弧才可以省略。输入参数的数量大于或者等于2时,Lambda表达式左边的一对小括弧中的多个参数质检使用逗号(,)分割。 示例1 下面创建一个Lambda表达式,它的输入参数的数量为0.该表达式将显示“This is a Lambda expression”字符串。 [csharp] view plaincopyprint? ()=>Console.WriteLine("This is a Lambda expression."); ()=>Console.WriteLine("This is a Lambda expression."); 分析2 由于上述Lambda表达式的输入参数的数量为0,因此,该Lambda表达式的左边部分的一对小括弧不能被省略。 示例2 下面创建一个Lambda表达式,它的输入参数包含一个参数:m。该表达式将计算m参数与2的乘积。 [csharp] view plaincopyprint? m=>m*2; m=>m*2; 分析2 上述Lambda表达式的输入参数省略了一对小括弧,它与“(m)=>m*2”Lambda表达式是等效的。 示例3

Lambda

Lambda演算 Lambda演算是一个形式系统,它被设计出来用来研究函数定义,函数应用和递归。它是在二十世纪三十年代由Alonzo Church 和 Stephen Cole Kleene发明的。Church在1936年使用lambda演算来证明了判定问题是没有答案的。Lambda演算可以用来清晰的定义什么是一个可计算的函数。两个lambda演算表达式是否相等的问题不能够被一个通用的算法解决,这是第一个问题,它甚至排在停机问题之前。为了证明停机问题是没有答案的,不可判定性能够被证明。Lambda演算对于函数式编程语言(例如lisp)有重大的影响。 同时,数理逻辑中对于lambda演算的介绍就简单得多: λ-演算可以说是最简单、最小的一个形式系统。它是在二十世纪三十年代由Alonzo Church 和Stephen Cole Kleene发明的。至今,在欧洲得到了广泛的发展。可以说,欧洲的计算机科学是从λ-演算开始的,而现在仍然是欧洲计算机科学的基础,首先它是函数式程序理论的基础,而后,在λ-演算的基础上,发展起来的π-演算、χ-演算,成为近年来的并发程序的理论工具之一,许多经典的并发程序模型就是以π-演算为框架的。 这里不由得想起一位我尊敬的老师的博士毕业论文就是关于π-演算的,可惜这位老师已经去别的学校了。 Lambda演算表达了两个计算机计算中最基本的概念“代入”和“置换”。“代入”我们一般理解为函数调用,或者是用实参代替函数中的形参;“置换”我们一般理解为变量换名规则。后面会讲到,“代入”就是用lambda演算中的β-归约概念。而“替换”就是lambda演算中的α-变换。Lambda演算系统的形式化定义 维基百科全书上面的对于lambda演算的定义不是很正规,但是说明性的文字较多。而数理逻辑中的定义很严密,不过没有说明不容易理解。我尽可能把所有资料结合起来说明lambda演算系统的定义。字母表 lambda演算系统中合法的字符如下: 1. x1,x2,x3,…变元(变元的数量是无穷的,不能在有限步骤内穷举,这个很重要,后面有定理是根据这一点证明的) 2. à 归约 3. =等价 4. λ,(,)(辅助工具符号,一共有三个,λ和左括号右括号) 所有能够在lambda演算系统中出现的合法符号只有以上四种,其他符号都是非法的。例如λx.x+2,如果没有其他对于+符号的说明,那么这就是一个非法的λ演算表达式。 λ-项 λ-项在一些文章中也称为λ表达式(lambda expression),它是由上面字母表中的合法字符组成的表达式,合法的表达式组成规则如下: 1. 任一个变元是一个项 2. 若M,N是项,则(MN)也是一个项(function application,函数应用) 3. 若M是一个项,而x是一个变元,则(λx.M)也是一个项(function abstraction,函数抽象) 4. 仅仅由以上规则归纳定义得到的符号串是项 说明1:λ-项是左结合的,意思就是若f x y都是λ-项,那么f x y=(f x) y 说明2:(λx.M)这样的λ-项被称为函数抽象,原因是它常常就是一个函数的定义,函数的参数就是变量x,函数体就是M,而函数名称则是匿名的。用一个不恰当的比喻来说,我们通常认为的函数

Lambda 表达式

本文导读:“Lambda 表达式”是一个匿名函数,它可以包含表达式和语句,并且可用于创建委托或表达式树类型。所有 Lambda 表达式都使用 Lambda 运算符 =>。该Lambda 运算符的左边是输入参数(如果有),右边包含表达式或语句块。 lambda表达式使得函数可以在使用的地方声明,并且可以在lambda函数中使用lambda函数之外的数据。 一、lambda运算符 所有的lambda表达式都是用新的lambda运算符 " => ",可以叫他,“转到”或者“成为”。运算符将表达式分为两部分,左边指定输入参数,右边是lambda的主体。 使用Lambda表达式,当编译器编译时,Lambda表达式同样会被编译成一个匿名方法进行相应的操作,但是与匿名方法相比,Lambda表达式更容易阅读。 比较Lambda表达式和匿名方法,在匿名方法中,"("、")"内是方法的参数的集合,这就对应了Lambda表达式中的"(参数列表)",而匿名方法中"{"、"}"内是方法的语句块,这对应了Lambda表达式中"=>"符号右边的表达式或语句块项。 二、lambda表达式语法 Lambda表达式可以有多个参数、一个参数,或者没有参数。 格式: (参数列表)=>表达式或语句块 表现形式为 1.一个参数:param=>expr 2.多个参数:(param-list)=>expr Lambda表达式的格式实例 1(x, y) => x * y //多参数,隐式类型=> 表达式 2x => x * 5 //单参数,隐式类型=>表达式

3x => { return x * 5; } //单参数,隐式类型=>语句块 4(int x) => x * 5 //单参数,显式类型=>表达式 5(int x) => { return x * 5; } //单参数,显式类型=>语句块 6() => Console.WriteLine() //无参数 三、Lambda表达式与匿名方法的对比实例 1、Sort方法 C# 代码复制 List list=new List(); var numbers = new []{ 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 }; list.AddRange(numbers); list.Sort(delegate (int a, int b) { return https://www.wendangku.net/doc/7513043544.html,pareTo(b); } ); //use lambda list.Sort((a,b)=>https://www.wendangku.net/doc/7513043544.html,pareTo(b)); 2、ConvertAll方法 C# 代码复制 ListdoubleList =list.ConvertAll(delegate(int i) { return i*2; }); //use lambda var doubleList2=list.ConvertAll(i=>i*2);

lambda演算实例

lambda演算实例 关于lambda演算的定义和解释的确有点让人迷糊,主要不是因为lambda演算有多复杂,而是一些基本概念没有归入正确位置的原因。 这里先写一点草稿,在实践中学习和领悟lambda演算到底是个什么东西。 一:自然数运算: 在lambda演算中的邱奇数定义 0 = λf.λx.x 1 = λf.λx.f x 2 = λf.λx.f (f x) 3 = λf.λx.f (f (f x)) SUCC = λn.λf.λx.f(n f x) SUCC是一个有三个自由变量的函数 计算 SUCC 0 =λn.λf.λx.f (n f x) 0 //将0代入n =λf.λx.f (0 f x) //0=λf.λx.x =λf.λx.f (λf.λx.x f x) //λf.λx.x f x是将两个参数的函数λf.λx.x应用于(f x)这两个值的结果,结果等于x =λf.λx.f x //正好等于1的邱奇数定义 SUCC 1 =λn.λf.λx.f (n f x) 1 //将1代入n =λf.λx.f (1 f x) //0=λf.λx.x =λf.λx.f (λf.λx.(f x) f x) //λf.λx.(f x) f x是将两个参数的函数λf.λx.(f x)应用于(f x)这两个值的结果,结果等于(f x) =λf.λx.f (f x) //正好等于2的邱奇数定义 小节: 不妨把lambda演算看做一个自动机,你输入一个式子(一个λ项),它就给你输出一个演算结果,至于输入和输出有什么意义,是我们自己赋予的。 比如为了计算0的后继,我们只是输入(λn.λf.λx.f(n f x) λf.λx.x)给这个机器,这个机器就会给我们输出λf.λx.f x。在解释这个输入输出关系时,我们会说,SUCC 0 = 1,这样就赋予这个运算一个意义。这个自动机知道自己在做加1操作吗?其实它什么也不知道。 为什么邱奇数这样定义?其实不妨把它们看做是被偶然发现的一些λ项,这些λ项的组合项的演

C++11 新特性:Lambda 表达式

C++11 新特性:Lambda 表达式 作者: DevBean 日期: 2012 年05 月15 日发表评论 (1)查看评论 参考文章:https://https://www.wendangku.net/doc/7513043544.html,/pcarlini/entry/c_1x_tidbits_lambda_expressions 或许,Lambda 表达式算得上是C++ 11 新增特性中最激动人心的一个。这个全新的特性听起来很深奥,但却是很多其他语言早已提供(比如C#)或者即将提供(比如Java)的。简而言之,Lambda 表达式就是用于创建匿名函数的。GCC 4.5.x 和Microsoft Visual Studio 早已提供了对lambda 表达式的支持。在GCC 4.7 中,默认是不开启C++ 11 特性的,需要添 加 -std=c++11 编译参数。而VS2010 则默认开启。 为什么说lambda 表达式如此激动人心呢?举一个例子。标准C++ 库中有一个常用算法的库,其中提供了很多算法函数,比如sort() 和find()。这些函数通常需要提供一个“谓词函数predicate function”。所谓谓词函数,就是进行一个操作用的临时函数。比如find() 需要一个谓词,用于查找元素满足的条件;能够满足谓词函数的元素才会被查找出来。这样的谓词函数,使用临时的匿名函数,既可以减少函数数量,又会让代码变得清晰易读。 下面来看一个例子: #include #include void abssort(float*x, unsigned N) { std::sort(x, x + N, [](float a, float b){return std::abs(a)< std::abs(b);}); } 从上面的例子来看,尽管支持lambda 表达式,但C++ 的语法看起来却很“神奇”。lambda 表达式使用一对方括号作为开始的标识,类似于声明一个函数,只不过这个函数没有名字,也就是一个匿名函数。这个匿名函数接受两个参数,a和b;其返回值是一个bool 类型的值,注意,返回值是自动推断的,不需要显式声明,不过这是有条件的!条件就是,lambda 表达式的语句只有一个return。函数的作用是比较a、b 的绝对值的大小。然后,在此例中,这个lambda 表达式作为一个闭包被传递给std::sort()函数。 下面,我们来详细解释下这个神奇的语法到底代表着什么。 我们从另外一个例子开始: std::cout<<[](float f){return std::abs(f);}(-3.5); 输出值是什么?3.5!注意,这是一个函数对象(由lambda 表达式生成),其实参是-3.5,返回值是参数的绝对值。lambda 表达式的返回值类型是语言自动推断的,因为std::abs() 的返回值就是float。注意,前面我们也提到了,只有当lambda 表达式中的语句“足够简单”,才能自动推断返回值类型。

层次分析法各权重计算的MATLAB程序

v1.0 可编辑可修改层次分析法各权重计算的MATLAB程序 clc clear %修改对比矩阵、一致性检验就可以 a=[1,1,1,4,1,1/2 1,1,2,4,1,1/2 1,1/2,1,5,3,1/2 1/4,1/4,1/5,1,1/3,1/3 1,1,1/3,3,1,1 2,2,2,3,3,1]; [x,y]=eig(a);eigenvalue=diag(y);lamda=eigenvalue(1); ci1=(lamda-6)/5;cr1=ci1/ w1=x(:,1)/sum(x(:,1)) b1=[1,1/4,1/2;4,1,3;2,1/3,1]; [x,y]=eig(b1);eigenvalue=diag(y);lamda=eigenvalue(1); ci21=(lamda-3)/2;cr21=ci21/ w21=x(:,1)/sum(x(:,1)) b2=[1 1/4 1/5;4 1 1/2;5 2 1]; [x,y]=eig(b2);eigenvalue=diag(y);lamda=eigenvalue(1); ci22=(lamda-3)/2;cr22=ci22/ w22=x(:,1)/sum(x(:,1)) b3=[1 3 1/3;1/3 1 1/7;3 7 1];

v1.0 可编辑可修改[x,y]=eig(b3);eigenvalue=diag(y);lamda=eigenvalue(1); ci23=(lamda-3)/2;cr23=ci23/ w23=x(:,1)/sum(x(:,1)) b4=[1 1/3 5;3 1 7;1/5 1/7 1]; [x,y]=eig(b4);eigenvalue=diag(y);lamda=eigenvalue(1); ci24=(lamda-3)/2;cr24=ci24/ w24=x(:,1)/sum(x(:,1)) b5=[1 1 7;1 1 7;1/7 1/7 1]; [x,y]=eig(b5);eigenvalue=diag(y);lamda=eigenvalue(2); ci25=(lamda-3)/2;cr25=ci25/ w25=x(:,2)/sum(x(:,2)) b6=[1 7 9;1/7 1 1 ;1/9 1 1]; [x,y]=eig(b6);eigenvalue=diag(y);lamda=eigenvalue(1); ci26=(lamda-3)/2;cr26=ci26/ w26=x(:,1)/sum(x(:,1)) w_sum=[w21,w22,w23,w24,w25,w26]*w1 ci=[ci21,ci22,ci23,ci24,ci25,ci26]; cr=ci*w1/sum*w1)

相关文档