Lambda的前世
假如您大学时代的 离散数学或者 编译原理 还没完全还给老师,那不妨看下下面的一段话:
λ(Lambda演)算是一套用于研究函数定义、函数应用和递归的形式系统。它由 Alonzo Church 和 Stephen Cole Kleene 在 20 世纪三十年代引入,Church 运用 lambda 演算在 1936 年给出判定性问题 (Entscheidungsproblem) 的一个否定的答案。这种演算可以用来清晰地定义什么是一个可计算函数。关于两个 lambda 演算表达式是否等价的命题无法通过一个通用的算法来解决,这是不可判定性能够证明的头一个问题,甚至还在停机问题之先。Lambda 演算对函数式编程有巨大的影响,特别是Lisp 语言。
Lambda 演算可以被称为最小的通用程序设计语言。它包括一条变换规则 (变量替换) 和一条函数定义方式,Lambda 演算之通用在于,任何一个可计算函数都能用这种形式来表达和求值。因而,它是等价于图灵机的。尽管如此,Lambda 演算强调的是变换规则的运用,而非实现它们的具体机器。可以认为这是一种更接近软件而非硬件的方式。
看不懂 没关系,因为根本没必要看懂,就像饭馆的食客没必要会做菜一样,我们依旧可以把Lambda 吃得津津有味。
匿名方法
C# 2.0推出了匿名方法,而Lambda表达式则是对匿名方法一个更简化的书写,同时也是匿名方法的一个超集。让我们先来复习匿名方法
(详细内容参见 )
List<string> stringList = new List<string> { "Burke", "Connor", "Frank", "Everett" };
stringList.ForEach(delegate(string item)
{
this.richTextBoxShow.AppendText(item+""n");
});
在匿名方法中直接以一个 delegate申明一个委托,string item申明其接受一个string类型的参数,并可以在匿名方法代码块中使用。
Lambda表达式
再来看下Lambda表达式的用法
List<string> stringList = new List<string> { "Burke", "Connor", "Frank", "Everett" };
stringList.ForEach(item => this.richTextBoxShow.AppendText(item + ""n"));
其中 item => this.richTextBoxShow.AppendText(item + ""n")就是一个Lambda表达式,item是隐式申明的入参item的类型由编译器推断出, =>右侧就是一个表达式,也可以是一个代码块{ this.richTextBoxShow.AppendText(item + ""n"); }
下面就来看看Lambda表达式的一种书写形式
参数部分可以是隐式申明的,也可以是显示申明的
(x) => int y = x + 1 //隐式申明
(int x) => int y = x + 1 //显示申明
当参数是一个是,()可以省略
x => return y = x + 1
(x, y) => return z = x + y
代码块部分,当只有一句语句时,使用纯粹的表达时 就可以了,而超过两句,则需用代码块的形式
m => int z = m+2 //表达式
m => { int z = m +2; int y = z*z }; //代码块
Lambda表达式是匿名方法的一个超集,相比匿名方法提供了更多的功能
Lambda表达式允许参数类型被删除和推断,而匿名函数需要参数类型的显式声明。
Lambda表达式的主体可以是一个表达式或者是一个声明块,而匿名函数的主体只能是声明块。
Lambda表达式的主体可以是一个表达式或者是一个声明块,而匿名函数的主体只能是声明块。
Lambda的高阶用法
Lambda最为匿名方法的超集,匿名方法所具有的内联参数等都是继承了的,那它的灵活性又怎么样呢?网上的资料似乎也只是把它当作写代码块的形式,那能不能玩出点花样呢?本着RD探索的精神,尝试了递归和嵌套的写法
递归
我尝试用Lambda表达式来完成一个分解质因数的算法
LambdaDelegateVoidLong delegateVoidLong = null;
delegateVoidLong = (m) =>
{
for (long i = 2; i <= m; i++)
{//遍历 比自身的 小的数
if (m == i)
{
this.richTextBoxShow.AppendText(m.ToString() +""n"); //如果比对到自己,说明已经无法再 分解,则输出质因数
}
else //如果尚未比对到自己,则测试是否是自身的因数
{
long lest = m % i;
if (lest == 0)//如果是因数,则递归分解质因数
{
delegateVoidLong(m / i);
delegateVoidLong(i);
break;
}
}
}
};
this.richTextBoxShow.AppendText("分解质因数" + MAX.ToString() + ""n");
delegateVoidLong(MAX.Value);
在这段代码中,为了能自己调用自己,必须首先申明委托对象
LambdaDelegateVoidLong delegateVoidLong = null;
而不是象如下这样,在声明的 同时定义代码块,因为这样在代码块中调用自身的委托对象delegateVoidLong会报编译器错误 ,事先声明一下就OK了。由理由相信这是VS 2008 bate2版存在的编译器bug。
LambdaDelegateVoidLong delegateVoidLong = (m) => { }
嵌套
在Lambda表达式的数学含义就是定义函数的形式表达,Lambda 算子计算规则有alpha和beta。Alpha规则又叫转换(conversion)规则,而beta规则又叫简化(reduction)规则。
在 Lambda表达式中,变量的名字是不重要的,一次可以无限次转换和简化。冲着Lambda灵活的数学表达意义,原以为C#3.0Lambda表达式也能带来无限灵活的表达意义,但事实总是让人失望,至少目前的尝试是这样。
LambdaDelegateIntIntInt delegateIntIntInt = (m, n) => {
int z = (m + n);
LambdaDelegateIntInt delegateIntInt = (f) => { return f + 1; };
return delegateIntInt(z);
};
delegateIntIntInt(2, 3);
在上面这段代码中,试图在一段Lambda表达式delegateIntIntInt中,套用另一个Lambda表达式delegateIntInt。因为参数只能是变量,而不能是表达式,因此如数学概念中的 (m, n)=>(m+n, m-n)=>{ m+n/m-n };的表达式至少目前是无法写出的。