Lambda表达式及Lambda表达树[转]

(懒人直接就转载了)

Lambda表达式的概念

什么是lambda表达式?Lambda 表达式是一种匿名函数,它可以包含表达式和语句,并且可用于创建委托或

表达式目录树类型。我们使用lambda表达式可以帮助我们编写精简和紧凑的代码,许多操作中允许自定义排序和过

滤的函数,在.NET2.0的时候通常使用委托函数来实现,在.NET3.5可以使用lambda表达式。

现在举例说明lambda表达式:  Func<int,int> addOne= item=> item+1 ,其中操作符 “=>”读作“Goes to”,

可以理解为操作符左边的是函数的参数,操作符右边是函数体内容。上面我们定义的lambda表达式等同于函数如下:

 int addOne(int item)
{
     return item+1;
}

那么什么样的表达式才是合法的lambda表达式呢?

1. lambda表达式可以是多个参数。 如:  (item1,item2)=>item1+item2;

2. lambda表达式可以是0个参数。 如: ()=>"csharp";

3. lambda表达式可以显示指定参数类型。 (int item1,string item2)=>item1+item2;

4. lambda表达式函数体可以使用多条语句. (item1)=>{string ret="hello"+item1;return ret;};

 

使用lambda表达式的时候,不得不提到泛型委托。在上面我们定义的表达式如:(item1,item2)=>item1+item2;

只是定义的表达式,我们如何调用呢?我们可以定义自己的函数委托来引用lambda表达式,如下

public delegate int addOneDelegate(int item1,int item2);
void Main()
{
    addOneDelegate fun=(item1,item2)=>item1+item2;
    var result=fun(123,456);
    result.Dump("结果");
}
.结果
 579 
    
    在这里我们可以使用.NET类库中已经提供的泛型委托Func<T>和Action<T>来引用lambda表达式.代码如下
void Main()
{
    Func<int,int> fun=(item1,item2)=>item1+item2;
    var result=fun(123,456);
    result.Dump("结果");
}

.结果
 579
   关于Func<T>是泛型委托,最后的一个类型是指返回结果的类型,前面都是输入参数类型,上面的例子中,我们的输入类型是INT,返回类型也是INT。同样如果我们定义Func<int,string,bool>,是指输入参数有两个,一个是int类型,一个是string类型,函数返回是bool类型。使用泛型委托可以帮助我们方便引用lambda表达式。Func<T>提供了多个重载,如
  public delegate T Func<T>();
 public delegate T Func<A0, T>(A0 arg0);
 public delegate T Func<A0, A1, T>(A0 arg0, A1 arg1);
 public delegate T Func<A0, A1, A2, T>(A0 arg0, A1 arg1, A2 arg2);
  public delegate T Func<A0, A1, A2, A3, T>(A0 arg0, A1 arg1, A2 arg2, A3 arg3);
   
   在这里,需要提到一些关于lambda表达式的特性和规则。

1. lambda表达式的引用变量必须是显式类型。编译器对lambda表达式的类型推断是通过返回的引用变量的类型指定。

如下面的语句是非法的。

  void Main()
{
    var c=n=>n+1;//Error,Cannot assign lambda expression to an implicitly-typed local variable
     Func<string> cc=>n+1;//Ok

 }

 

2. 在lambda表达式中可以直接访问本地变量和全局变量。 

public static string grobalVar="grobal string";
void Main()
{
    string localVar="local string";
    Func<string,string> fun= n=> n+" can access "+grobalVar+" and "+localVar;
    fun("lambda").Dump();
}


结果:
lambda can access grobal string and local string 

3. lambda表达式的参数可以是ref或out方式传入,在通过ref或out方式传入的时候必须指定参数的具体类型。

public delegate int  RefParameterFunction(ref int n);
 void Main()
  {
    int x=10;
    RefParameterFunction  fun= (ref int n)=> n++;
    fun(ref x);
    x.Dump();
}   
结果:11 

4. lambda表达式的参数可以支持不定参数数传入。

 public delegate int AddFunction(params int[] ints);
 void Main()
  {
      int[] x={1,2,3,4,5,6,7,8,9};
      AddFunction fun= (items)=> 
      {  
             int count=0;
            foreach(int item in items)

                        {count=count+item;} 
                        return count;
                 };
     fun(x).Dump("求和:");
 } 
求和:45

Lambda表达式树的概念和示例

 

Lambda另一个强大的特性就是表达式树,lambda表达式都可以通过表达式树来描述,就不用在代码

中直接编写表达式。这样的优势就是表达式可以在运行的时候编译运行,而且可以对lambda表达式进行动态修改。

要使用lambda表达式树,首先提到一个表达式的泛型类Expression<T>,(域名空间System.Linq.Expressions),

这个类是保存表达式的结构信息。我们把Expession看作一棵树结构,每个结点都是由两部分组成,左树和右树,一直这样

递归下去。这里需要说明一下,刚开始使用表达式树的时候容易和表达式产生混淆,比如:

 1 void Main()
2 {
3
4     Expression<Func<int,int>> tree = x=>x+1;
5     Func<int,int> exp= x=> x+1;
6     
7     tree(1);//'tree' is a 'variable' but is used like a 'method'
8      exp(1);//输出2
9  }

注意:tree只是lambada表达式的树形结构信息,并不是函数可以直接调用。 

现在我们对lambda表达式的树结构输出来查看下,举例:我们编写一个验证三角形是否直角三角形,通过沟谷定律,

我们很容易编写lambda表达式为

1 (x,y,z)=> (x*x+y*y)==z*z

现在我们使用LINQPad的Dump()函数进行输出显示:

1 void Main()
2 {
3     Expression<Func<int,int,int,bool>> tree =  (x,y,z)=>(x*x+y*y)==z*z;
4     tree.Dump();
5 }

输出结果如下:

通过输出的图形,我们可以清楚的看出整个lambda表达式是由LEFT和RIGHT两部分组成的,Left部分和right部分之间

的关系通过 NodeType属性指定,所有的NodeType类型通过枚举(System.Linq.Expressions.ExpressionType )定义,

而结点的Type可以看作返回类型,比如我们定义的  tree的Type是Func<int,int,int,bool>,而Type是Lambda。

那么如何把表达式树转换为可以直接使用的函数呢?Expression类提供了函数Compile(),就可以把我们定义的lambda

表达式树编译为实际的函数,代码如下:

 1 void Main()
2 {
3     Expression<Func<int,int,int,bool>> tree =  (x,y,z)=>(x*x+y*y)==z*z;
4     Func<int,int,int,bool> fun= tree.Compile();
5     fun(3,4,5).Dump();
6 }
7   
9 结果
10 True 

我们了解到了lambda表达式树的基本概念,现在我们自行构造一个lambda表达式树。还是以上面的验证是否是直角

三角形为例,我们通过System.Linq.Expressions提供了表达式类来构造这个表达式,不参考LINQPad输出的结构。现在

我们分析表达式的树结构,(x,y,z) => (x*x + y*y)== z*z 按照操作符把表达式分为left tree和right tree。比如首先

我们把整个表达式分为左树:x*x + y*y,  右树:z*z, 关系:Equal,以此画出阿里如下:

我们已经把表达式树分析出来,现在我们开始使用.NET提供的表达式类来构造这棵表达式树,在这棵树比较简单,

我们比较用到的类包括二元表达式类(BinaryExpression)和参数表达式类(ParameterExpression)。现在我们

从树的叶结点开始构造 

首先我们需要制定表达式中参数和参数的类型。

1 ParameterExpression expX= Expression.Parameter(typeof(int),"x");
2 ParameterExpression expY= Expression.Parameter(typeof(int),"y");
3 ParameterExpression expZ= Expression.Parameter(typeof(int), "z");
  接着我们使用二元表达式将参数表达式关联起来,X和X,Y和Y,Z和Z,二元关系都是乘. 
1 BinaryExpression mulX = Expression.Multiply(expA, expA);
2 BinaryExpression mulY=  Expression.Multiply(expY, expY);
3 BinaryExpression mulZ=  Expression.Multiply(expZ, expZ);

然后我们将X*X+Y*Y通过 加二元表达式关联起来.

1 BinaryExpression addXY = Expression.Add(mulX,mulY)

最后我们将X*X+Y*Y 和Z*Z通过 等于二元表达式关联起来.

BinaryExpression final= Expression.Equal(mulZ, addXY);

现在我们构造完成后,可以通过编译来执行,下面是完整的代码:

1 void Main()
2 {
3     ParameterExpression expX= Expression.Parameter(typeof(int),"x");
4     ParameterExpression expY= Expression.Parameter(typeof(int),"y");
5     ParameterExpression expZ= Expression.Parameter(typeof(int), "z");
6     BinaryExpression mulX= Expression.Multiply(expX, expX);
7     BinaryExpression mulY= Expression.Multiply(expY, expY);
8     BinaryExpression mulZ= Expression.Multiply(expZ, expZ);
9     BinaryExpression addXY = Expression.Add(mulX,mulY);
10     BinaryExpression final= Expression.Equal(mulZ, addXY);
11     Expression<Func<int, int, int, bool>> square =
                Expression.Lambda
<Func<int, int, int, bool>>(final, expX, expY, expZ);
12     Func<int, int, int, bool> xx= square.Compile();
13     xx(3,4,5).Dump();
14 }
15
16
17 结果:
18 True 

Lambda表达式的简单应用 

1. 对数组的自定义排序。

 void Main()
2 {
3     string[] items={"csharp","cpp","python","perl","java"};
4     List<string> list=items.ToList();
5     list.Sort((x,y)=>y.Length-x.Length);
6    
7     list.Dump();
8 }
9
10 结果:
11 python
12 csharp
13 perl
14 java
15 cpp    

2. 对数组数据进行搜索

 1 void Main()
2 {
3     string[] items={"csharp","cpp","python","perl","java"};
4     List<string> list=items.ToList();
5     var result=    list.FindAll(x=> x.Length==4);
6    
7     result.Dump();
8 }
9
10 结果:
11 perl
12 java

 
3. 对数组数据进行直接更新
 1 void Main()
2 {
3     string[] items={"csharp","cpp","python","perl","java"};
4     var result= items.Select(n=> n+" : "+n.Length);
5     result.Dump();
6 }
7
8 结果:
9 csharp : 6
10 cpp : 3
11 python : 6
12 perl : 4
13 java : 4
posted on 2011-05-08 22:52  windfree  阅读(939)  评论(0)    收藏  举报