金沙国际网址 > 摄影 > 实际上Lambda表达式的本质是匿名方法,"匿名方法

原标题:实际上Lambda表达式的本质是匿名方法,"匿名方法

浏览次数:198 时间:2019-11-29

配置参数

  • 特色:视瑞特ST-56D/O 佳能5D3录影监视器5.6寸取景器1280*800超高清

查看完整参数>>

      前面复习了"隐式类型局部变量","扩展方法","对象和集合初始化器","匿名方法"知识点。见C# 3.0语言增强学习笔记(一)

原文出处:

实际上Lambda表达式的本质是匿名方法,"匿名方法"知识点。OpenCascade BRep Format Description

eryar@163.com

摘要Abstract:本文结合OpenCascade的BRep格式描述文档和源程序,对BRep格式进行分析,详细说明BRep的数据组织形式。结合源程序,可以对OpenCascade中Modeling Data模块中的模型数据结构进行理解。 

关键字Key Words:OpenCascade, BRep Format, ModelingData 

  

这里我们另外复习C# 3.0的其他新特性:Lambda表达式

在C#2.0中引入了匿名方法,允许在期望出现委托的时候以“内联”的代码替代之。尽管匿名方法提供了函数式编程语言中的很多表达能力,但匿名方法的语法实在是太罗嗦了,并且很不自然。Lambda表达式为书写匿名方法提供了一种更加简单、更加函数化的语法。

一、引言 Introduction

OpenCascade中的BRep格式主要用来存储3D模型,也可用来存储由下列元素组成的模型:vertices, edges, wires, faces, shells, solids, compsolids, compounds, edge triangulations, face triangulations, polylines on triangulations, space location and orientation. 

本格式的目的就是为了便于理解,也使用了类似BNF的定义方式。以下章节都是按下面的格式组织的: 

l 该部分的示例; 

l 该部分的类BNF定义; 

l 该部分的详细说明; 

l 该部分的源程序片段。 

  

初识Lambda表达式:

实际上Lambda表达式的本质是匿名方法,也即是当编译我们的程序代码时,编译器会自动帮我们将Lambda表达式转换为匿名方法。

二、通用结构 Format Common Structure

BRep格式文件的读写采用了ASCII的编码方式,该格式的数据都是文本形式存储。 

BRep格式使用了下面的BNF术语: 

1) <n>:换行; 

2) <_n>: 

3) <_>:空格; 

4) <flag>:标志位:0和1; 

5) <int>:整数,范围-231到231-1; 

6) <real>:实数,范围-1.7976931348623159X10308到1.7976931348623158X10308; 

金沙国际网址,7) <2D point>:二维点,两个实数; 

8) <3D point>:三维点,三个实数; 

9) <2D direction>:二维方向矢量,两个实数,平方和为1,即为单位方向矢量; 

10) <3D direction>:三维方向矢量,三个实数,平方和为1,即为单位方向矢量; 

11) <+> 

BRep格式包含以下部分: 

1) <content type> 

2) <version> 

3) <locations> 

4) <geometry> 

5) <shapes> 

<content type>部分: 

金沙国际网址 1

<content type>也可以有其它的值。 

<version>部分: 

金沙国际网址 2

不同版本之间的区别将会在本文档中说明。 

  

使用C# 2.0中的匿名方法查找"内部包含abc子串的所有字符串": 

20.5.1 创建Lambda表达式

Lambda表达式的书写方式是一个参数列表后跟“=>”记号,然后跟一个表达式或一个语句块,即Lambda表达式的语法格式为:

参数列 => 语句或语句块

Lambda表达式例子如下所示:

delegate int del(int i);

 ...

 del myDelegate = x => x * x;

 int j = myDelegate(5); //j = 25

关于“参数列”,Lambda表达式的参数列可以具有显式的或隐式的类型。在一个具有显式类型的参数列表中,每个参数的类型都是显式声明的。在一个具有隐式类型的参数列表中,参数的类型是从Lambda表达式出现的上下文中推断出来的——具体来说,是当Lambda表达式被转换为一个兼容的委托类型时,该委托类型提供了参数的类型。

当Lambda表达式只有一个具有隐式类型的参数时,参数列表中的括号可以省略。即:

(param) => expr

可以简写为:

param => expr

最后,参数列中可包含任意个参数(与委托对应),如果参数列中有0个或1个以上参数,则必须使用括号括住参数列,如下:

() => Console.Write("0个参数");

 i => Console.Write("1个参数时参数列中可省略括号,值为:{0}", i);

 (x, y) => Console.Write("包含2个参数,值为:{0}:{1}", x, y);

而“语句或语句块”中如果只有一条语句,则可以不用大括号括住,否则则必须使用大括号,如下所示:

 i => Console.Write("只有一条语句");

 i => { Console.Write("使用大括号的表达式"); };

 //两条语句时必须要大括号

i => { i++; Console.Write("两条语句的情况"); };

如果“语句或语句块”有返回值时,如果只有一条语句则可以不写“return”语句,编译器会自动处理,否则必须加上,如下示例:

class Test

    {

        delegate int AddHandler(int x, int y);

        static void Print(AddHandler add)

        {

            Console.Write(add(1, 3));

        }

        static void Main()

        {

            Print((x, y) => x + y);

            Print((x, y) => { int v = x * 10; return y + v; });

            Console.Read();

        }

    }

Lambda表达式是委托的实现方法,所以必须遵循以下规则:

l         Lambda表达式的参数数量必须和委托的参数数量相同;

l         如果委托的参数中包括有ref或out修饰符,则Lambda表达式的参数列中也必须包括有修饰符;

 

我们来看如下例子:

 

class Test

    {

        delegate void OutHandler(out int x);

        static void Print(OutHandler test)

        {

            int i;

            test(out i);

            Console.Write(i);

        }

        static void Main()

        {

            Print((out int x) => x = 3);

            Console.Read();

        }

    }

l         如果委托有返回类型,则Lambda表达式的语句或语句块中也必须返回相同类型的数据;

l         如果委托有几种数据类型格式而在Lambda表达式中编译器无法推断具体数据类型时,则必须手动明确数据类型。

由上面可见,C# 2.0规范中提到的匿名方法规范同样适用于Lambda表达式。Lambda表达式是匿名方法在功能行上的超集,提供了下列附加的功能:

l         Lambda表达式允许省略参数类型并对其进行推断,而匿名方法要求参数类型必须显式地声明。

l         Lambda表达式体可以是表达式或语句块,而匿名方法体只能是语句块。

l         在类型参数推导和方法重载抉择时,Lambda表达式可以被作为参数传递。

l         以一个表达式作为表达式体的Lambda表达式可以被转换为表达式树。

三、<locations>部分 Section <locations>

示例: 

金沙国际网址 3

BNF 定义: 

金沙国际网址 4

详细说明: 

<location data 1>定义了3X4的矩阵Q,描述了三维空间的线性变换,并满足如下约定: 

金沙国际网址 5

矩阵Q是线性变换矩阵,它可以通过矩阵乘法将一个点(x, y, z)变换成另外一点(u, v, w): 

金沙国际网址 6

Q也可能是以下基本变换矩阵的组合: 

1) 平移变换矩阵: 

金沙国际网址 7

2) 绕任意轴旋转的变换矩阵,轴的方向为D(Dx, Dy, Dz),旋转角度ψ: 

金沙国际网址 8

3) 缩放变换矩阵: 

金沙国际网址 9

4) 中心对称变换矩阵: 

金沙国际网址 10

5) 轴对称变换矩阵: 

金沙国际网址 11

6) 平面对称变换矩阵: 

金沙国际网址 12

<location data 2>解释为组合变换的幂。<location data 2>是整数对li, pi的序列。这个序列将被解释为: 

金沙国际网址 13

Lli是<location record>部分的变换矩阵。 

读取<locations>部分的类为TopTools_LocationSet,程序代码如下所示:

 1 //=======================================================================
 2 //function : Read
 3 //purpose  : 
 4 //=======================================================================
 5 void  TopTools_LocationSet::Read(Standard_IStream& IS)
 6 {
 7   myMap.Clear();
 8 
 9   char buffer[255];
10   Standard_Integer l1,p;
11 
12   IS >> buffer;
13   if (strcmp(buffer,"Locations")) {
14     cout << "Not a location table "<<endl;
15     return;
16   }
17 
18   Standard_Integer i, nbLoc;
19   IS >> nbLoc;
20   
21   TopLoc_Location L;
22   gp_Trsf T;
23     
24   //OCC19559
25   Message_ProgressSentry PS(GetProgress(), "Locations", 0, nbLoc, 1);
26   for (i = 1; i <= nbLoc&& PS.More(); i++, PS.Next()) {
27     if ( !GetProgress().IsNull() ) 
28       GetProgress()->Show();
29 
30     Standard_Integer typLoc;
31     IS >> typLoc;
32     
33     if (typLoc == 1) {
34       ReadTrsf(T,IS);
35       L = T;
36     }
37 
38     else if (typLoc == 2) {
39       L = TopLoc_Location();
40       IS >> l1;
41       while (l1 != 0) { 
42     IS >> p;
43     TopLoc_Location L1 = myMap(l1);
44     L = L1.Powered(p) *L;
45     IS >> l1;
46       }
47     }
48     
49     if (!L.IsIdentity()) myMap.Add(L);
50   }
51 }

虽然代码风格不好,缩进、括号什么的都不工整,看起来很吃力,但是结合源程序,对上面的详细说明的理解还是很有帮助的。 

其中变量nbLoc是<location record count>的值,成员变量myMap是TopLoc_Location的一个map。当是<location record 1>时把<location data 1>都放到TopLoc_Location的map中。当是<location record 2>时将li的变换矩阵TopLoc_Location乘pi次方。<flag>0表示<location data 2>的结束。 

  

List.FindAll(
      delegate(string s){
      return s.Indexof("abc")>0}
)

20.5.2 Lambda表达式转换

和匿名方法表达式类似,Lambda表达式可以归类为一种拥有特定转换规则的值。这种值没有类型,但可以被隐式地转换为一个兼容的委托类型。特别地,当满足下列条件时,委托类型D兼容于Lambda表达式L:

l         D和L具有相同数量的参数;

l         如果L具有显式类型的参数列表,D中每个参数的类型和修饰符必须和L中相应的参数完全一致;

l         如果L具有隐式类型的参数列表,则D中不能有ref或out参数;

l         如果D具有void返回值类型,并且L的表达式体是一个表达式,若L的每个参数的类型与D的参数一致,则L的表达式体必须是一个可接受为statement-expression的有效表达式;

l         如果D具有void返回值类型,并且L的表达式体是一个语句块,若L的每个参数的类型与D的参数一致,则L的表达式体必须是一个有效语句块,并且该语句块中不能有带有表达式的return语句;

l         如果D的返回值类型不是void,并且L的表达式体是一个表达式,若L的每个参数的类型与D的参数一致,则L的表达式体必须是一个可以隐式转换为D的返回值类型的有效表达式;

l         如果D的返回值类型不是void,并且L的表达式体是一个语句块,若L的每个参数的类型与D的参数一致,则L的表达式体必须是一个有效的语句块,该语句块不能有可达的终点(即必须有return语句),并且每个return语句中的表达式都必须能够隐式转换为D的返回值类型。

后面的例子将使用一个范型委托F<U, T>表示一个函数,它具有一个类型为U的参数u,返回值类型为T:

delegate T F<U, T>(U u);

我们可以像在下面这样赋值:

F<int, int> f1 = x => x + 1;          

 F<int, double> f2 = x => x + 1;

每个Lambda表达式的参数和返回值类型通过将Lambda表达式赋给的变量的类型来检测。第一个赋值将Lambda表达式成功地转换为了委托类型Func<int, int>,因为x的类型是int,x + 1是一个有效的表达式,并且可以被隐式地转换为int。同样,第二个赋值成功地将Lambda表达式转换为了委托类型Func<int, double>,因为x + 1的结果(类型为int)可以被隐式地转换为double类型。

来看如下代码,如果这样赋值会怎么样?

F<double, int> f3 = x => x + 1;

我们运行上面的代码,编译器会报如下两条错误:

(1)无法将类型“double”隐式转换为“int”。存在一个显式转换(是否缺少强制转换?)。

(2)无法将Lambda表达式转换为委托类型“F<double,int>”,原因是块中的某些返回类型不能隐式转换为委托返回类型。

其实产生一个编译期错误原因是,x给定的类型是double,x + 1的结果(类型为double)不能被隐式地转换为int。

四、<geometry>部分

<geometry>包含以下子部分: 

1.<2D curves> 

2.<3D curves> 

3.<3D polygons> 

4.<polygons on triangulations> 

5.<surfaces> 

6.<triangulations> 

读取<geometry>部分的类为BRepTools_ShapeSet,程序代码如下所示:

 1 //=======================================================================
 2 //function : ReadGeometry
 3 //purpose  : 
 4 //=======================================================================
 5 void  BRepTools_ShapeSet::ReadGeometry(Standard_IStream& IS)
 6 {
 7   //OCC19559
 8   myCurves2d.SetProgress(GetProgress());
 9   myCurves.SetProgress(GetProgress());
10   mySurfaces.SetProgress(GetProgress());
11 
12   if ( !GetProgress().IsNull()) {
13     if( GetProgress()->UserBreak() ) return;
14     GetProgress()->NewScope ( 15, "2D Curves" );
15   }
16   myCurves2d.Read(IS);
17 
18   if ( !GetProgress().IsNull()) {
19     if( GetProgress()->UserBreak() ) return;
20     GetProgress()->EndScope();
21     GetProgress()->Show();
22     
23     GetProgress()->NewScope ( 15, "3D Curves" );
24   }
25   myCurves.Read(IS);
26 
27   if ( !GetProgress().IsNull()) {
28     if( GetProgress()->UserBreak() ) return;
29     GetProgress()->EndScope();
30     GetProgress()->Show();
31     
32     GetProgress()->NewScope ( 10, "3D Polygons" );
33   }
34   ReadPolygon3D(IS);
35   if ( !GetProgress().IsNull() ) {
36     if( GetProgress()->UserBreak() ) return;
37     GetProgress()->EndScope();
38     GetProgress()->Show();
39 
40     GetProgress()->NewScope ( 10, "Polygons On Triangulation" );
41   }
42   ReadPolygonOnTriangulation(IS);
43   if ( !GetProgress().IsNull()) {
44     if( GetProgress()->UserBreak() ) return;
45     GetProgress()->EndScope();
46     GetProgress()->Show();
47     
48     GetProgress()->NewScope ( 10, "Surfaces" );
49   }
50   mySurfaces.Read(IS);
51   if ( !GetProgress().IsNull() ) {
52     if( GetProgress()->UserBreak() ) return;
53     GetProgress()->EndScope();
54     GetProgress()->Show();
55 
56     GetProgress()->NewScope ( 15, "Triangulations" );
57   }
58   ReadTriangulation(IS);
59   if ( !GetProgress().IsNull()) {
60     if( GetProgress()->UserBreak() ) return;
61     GetProgress()->EndScope();
62     GetProgress()->Show();
63   }
64 }

 

 使用C# 3.0中的Lambda表达式查找"内部包含abc子串的所有字符串":

20.5.3 类型推断

当在没有指定类型参数的情况下调用一个范型方法时,一个类型推断过程会去尝试为该调用推断类型参数。被作为参数传递给范型方法的Lambda表达式也会参与这个类型推断过程。

最先发生的类型推断独立于所有参数。在这个初始阶段,不会从作为参数的Lambda表达式推断出任何东西。然而,在初始阶段之后,将通过一个迭代过程从Lambda表达式进行推断。特别地,当下列条件之一为真时将会完成推断:

l         参数是一个Lambda表达式,以后简称为L,从其中未得到任何推断;

l         相应参数的类型,以后简称为P,是一个委托类型,其返回值类型包括了一个或多个方法类型参数;

l         P和L具有相同数量的参数,P中每个参数的修饰符与L中相应的参数一致,或者如果L具有隐式类型的参数列表时,没有参数修饰符;

l         P的参数类型不包含方法类型参数,或仅包含于已经推断出来的类型参数相兼容的一组类型参数;

l         如果L具有显式类型的参数列表,当推断出来的类型被P中的方法类型参数取代了时,P中的每个参数应该具有和L中相应参数一致的类型。

l         如果L具有隐式类型的参数列表,当推断出来的类型被P中的方法类型参数取代了并且作为结果的参数类型赋给了L时,L的表达式体必须是一个有效的表达式或语句块。

l         可以为L推断一个返回值类型。

对于每一个这样的参数,都是通过关联P的返回值类型和从L推断出的返回值类型来从其上进行推断的,并且新的推断将被添加到累积的推断集合中。这个过程一直重复,直到无法进行更多的推断为止。

在类型推断和重载抉择中,Lambda表达式L的“推断出来的返回值类型”通过以下步骤进行检测:

l         如果L的表达式体是一个表达式,则该表达式的类型就是L的推断出来的返回值类型。

l         如果L的表达式体是一个语句块,若由该块中的return语句中的表达式的类型形成的集合中恰好包含一个类型,使得该集合中的每个类型都能隐式地转换为该类型,并且该类型不是一个空类型,则该类型即是L的推断出来的返回值类型。

l         除此之外,无法从L推断出一个返回值类型。

作为包含了Lambda表达式的类型推断的例子,请考虑System.Query.Sequence类中声明的Select扩展方法:

namespace System.Query

    {

        public static class Sequence

        {

            public static IEnumerable<S> Select<T, S>(

                this IEnumerable<T> source,

                Func<T, S> selector)

            {

                foreach (T element in source) yield return selector(element);

            }

        }

    }

假设使用using语句导入了System.Query命名空间,并且定义了一个Customer类,具有一个类型为string的属性Name,Select方法可以用于从一个Customer列表中选择名字:

List<Customer> customers = GetCustomerList();

 IEnumerable<string> names = customers.Select(c => c.Name);

对扩展方法Select的调用将被处理为一个静态方法调用:

IEnumerable<string> names = Sequence.Select(customers, c => c.Name);

由于没有显式地指定类型参数,将通过类型推断来推导类型参数。首先,customers参数被关联到source参数,T被推断为Customer。然后运用上面提到的拉姆达表达式类型推断过程,C的类型是Customer,表达式c.Name将被关联到selector参数的返回值类型,因此推断S是string。因此,这个调用等价于:

Sequence.Select<Customer, string>(customers, (Customer c) => c.Name);

并且其返回值类型为IEnumerable<string>。

下面的例子演示了Lambda表达式的类型推断是如何允许类型信息在一个范型方法调用的参数之间“流动”的。对于给定的方法:

static Z F<X, Y, Z>(X value, Func<X, Y> f1, Func<Y, Z> f2) { return f2(f1(value)); }

现在我们来写这样一个调用,来看看它的推断过程:

double seconds = F("1:15:30", s => TimeSpan.Parse(s), t => TotalSeconds);

类型推断过程是这样的:首先,参数"1:15:30"被关联到value参数,推断X为string。然后,第一个Lambda表达式的参数s具有推断出来的类型string,表达式TimeSpan.Parse(s)被关联到f1的返回值类型,推断Y是System.TimeSpan。最后,第二个Lambda表达式的参数t具有推断出来的类型System.TimeSpan,并且表达式t.TotalSeconds被关联到f2的返回值类型,推断Z为double。因此这个调用的结果类型是double。

4.1 子部分<3D curves>

示例: 

金沙国际网址 14

BNF定义: 

金沙国际网址 15

详细说明: 

由Curves开始,后面是曲线的数量,再下面是每条曲线的具体数据。 

读取<curves>部分的类为GeomTools_CurveSet,程序代码如下所示:

 1 #define LINE      1
  2 #define CIRCLE    2
  3 #define ELLIPSE   3
  4 #define PARABOLA  4
  5 #define HYPERBOLA 5
  6 #define BEZIER    6
  7 #define BSPLINE   7
  8 #define TRIMMED   8
  9 #define OFFSET    9
 10 //=======================================================================
 11 //function : ReadCurve
 12 //purpose  : 
 13 //=======================================================================
 14 Standard_IStream& GeomTools_CurveSet::ReadCurve(Standard_IStream& IS,
 15                         Handle(Geom_Curve)& C)
 16 {
 17   Standard_Integer ctype;
 18 
 19   try {
 20     OCC_CATCH_SIGNALS
 21     IS >> ctype;
 22     switch (ctype) {
 23 
 24     case LINE :
 25       {
 26         Handle(Geom_Line) CC;
 27         IS >> CC;
 28         C = CC;
 29       }
 30       break;
 31 
 32     case CIRCLE :
 33       {
 34         Handle(Geom_Circle) CC;
 35         IS >> CC;
 36         C = CC;
 37       }
 38       break;
 39 
 40     case ELLIPSE :
 41       {
 42         Handle(Geom_Ellipse) CC;
 43         IS >> CC;
 44         C = CC;
 45       }
 46       break;
 47 
 48     case PARABOLA :
 49       {
 50         Handle(Geom_Parabola) CC;
 51         IS >> CC;
 52         C = CC;
 53       }
 54       break;
 55 
 56     case HYPERBOLA :
 57       {
 58         Handle(Geom_Hyperbola) CC;
 59         IS >> CC;
 60         C = CC;
 61       }
 62       break;
 63 
 64     case BEZIER :
 65       {
 66         Handle(Geom_BezierCurve) CC;
 67         IS >> CC;
 68         C = CC;
 69       }
 70       break;
 71 
 72     case BSPLINE :
 73       {
 74         Handle(Geom_BSplineCurve) CC;
 75         IS >> CC;
 76         C = CC;
 77       }
 78       break;
 79 
 80     case TRIMMED :
 81       {
 82         Handle(Geom_TrimmedCurve) CC;
 83         IS >> CC;
 84         C = CC;
 85       }
 86       break;
 87 
 88     case OFFSET :
 89       {
 90         Handle(Geom_OffsetCurve) CC;
 91         IS >> CC;
 92         C = CC;
 93       }
 94       break;
 95       
 96     default:
 97       {
 98         Handle(Geom_Curve) CC;
 99         GeomTools::GetUndefinedTypeHandler()->ReadCurve(ctype,IS,CC);
100         C = CC;
101       }
102     }
103   }
104   catch(Standard_Failure) {
105 #ifdef DEB
106     Handle(Standard_Failure) anExc = Standard_Failure::Caught();
107     cout <<"EXCEPTION in GeomTools_CurveSet::ReadCurve(..)!!!" << endl;
108     cout << anExc << endl;
109 #endif
110     C = NULL;
111   }
112   return IS;
113 }

 

因为重载了操作符>>,使不同的类调用了不同的处理函数。 

因为读取点和方向用得很频繁,所以将读取点和方向的函数程序先列出如下所示:

1 //=======================================================================
 2 //function : ReadPnt
 3 //purpose  : 
 4 //=======================================================================
 5 static Standard_IStream& operator>>(Standard_IStream& IS, gp_Pnt& P)
 6 {
 7   Standard_Real X=0.,Y=0.,Z=0.;
 8   IS >> X >> Y >> Z;
 9   P.SetCoord(X,Y,Z);
10   return IS;
11 }
12 
13 //=======================================================================
14 //function : ReadDir
15 //purpose  : 
16 //=======================================================================
17 static Standard_IStream& operator>>(Standard_IStream& IS, gp_Dir& D)
18 {
19   Standard_Real X=0.,Y=0.,Z=0.;
20   IS >> X >> Y >> Z;
21   D.SetCoord(X,Y,Z);
22   return IS;
23 }

 

List.FindAll(s=>s.IndexOf("abc")>0)

20.5.4 重载抉择

参数列表中的Lambda表达式将影响到特定情形下的重载抉择(也称重载分析,重载解析等,即从几个重载方法中选择最合适的方法进行调用的过程)。

下面是新添加的规则:

对于Lambda表达式L,且其具有推断出来的返回值类型,当委托类型D1和委托类型D2具有完全相同的参数列表,并且将L的推断出来的返回值类型隐式转换为D1的返回值类型要优于将L的推断出来的返回值类型隐式转换为D2的返回值类型时,称L到D1的隐式转换优于L到D2的隐式转换。如果这些条件都不为真,则两个转换都不是最优的。

4.1.1 <3D curve record 1>-Line

示例: 

金沙国际网址 16

BNF定义: 

金沙国际网址 17

详细说明: 

<3D curve record 1>定义了直线。直线数据由一个三维点P和一个三维方向矢量D组成。通过点P且方向为D的直线由下面的参数方程来定义: 

金沙国际网址 18

示例数据表示的直线为通过点P(1,0,3),方向D(0,1,0),得到的参数方程为: 

金沙国际网址 19

读取直线部分的程序代码如下所示:

 1 //=======================================================================
 2 //function : ReadCurve
 3 //purpose  : 
 4 //=======================================================================
 5 static Standard_IStream& operator>>(Standard_IStream& IS,
 6                     Handle(Geom_Line)& L)
 7 {
 8   gp_Pnt P(0.,0.,0.);
 9   gp_Dir AX(1.,0.,0.);
10   IS >> P >> AX;
11   L = new Geom_Line(P,AX);
12   return IS;
13 }

 

 

20.5.5 表达式树

表达式树允许将Lambda表达式表现为数据结构而不是可执行代码。一个可以转换为委托类型D的Lambda表达式,也可以转换为一个类型为System.Linq.Expressions. Expression<D>的表达式树。将一个Lambda表达式转换为委托类型导致可执行代码被委托所生成和引用,而将其转换为一个表达式树类型将导致创建了表达式树实例的代码被发出。表达式树是Lambda表达式的一种高效的内存中数据表现形式,并且使得表达式的结构变得透明和明显。

如下面的例子将一个Lambda表达式分别表现为了可执行代码和表达式树。由于存在到Func<int, int>的转换,因此存在到Expression<Func<int, int>>的转换。代码如下所示:

using System.Linq.Expressions;

// 代码

 Func<int, int> f = x => x + 1;

 // 数据

 Expression<Func<int, int>> e = x => x + 1;

在这些赋值完成之后,委托f标识一个返回x + 1的方法,而表达式树e表示一个描述了表达式x + 1的数据结构。

4.1.2 <3D curve record 2>-Circle

示例: 

金沙国际网址 20

BNF定义: 

金沙国际网址 21

详细说明: 

<3D curve record 2>定义了圆。圆的数据包含一个三维点P,一个正交坐标系的三个轴的方向N,Dx,Dy,还有一个非负的实数r。其中点P为圆心坐标,圆位于平面的法向量为N的平面上,圆的半径为r。圆的参数方程如下所示: 

金沙国际网址 22

示例数据表示的圆为:圆心P(1,2,3),位于平面的法向量N(0,0,1),圆的方向Dx=(1,0,-0),Dy=(-0,1,0),半径r=4,其参数方向为: 

金沙国际网址 23

读取圆部分的程序代码如下所示:

 1 //=======================================================================
 2 //function : ReadCurve
 3 //purpose  : 
 4 //=======================================================================
 5 static Standard_IStream& operator>>(Standard_IStream& IS,
 6                     Handle(Geom_Circle)& C)
 7 {
 8   gp_Pnt P(0.,0.,0.);
 9   gp_Dir A(1.,0.,0.),AX(1.,0.,0.),AY(1.,0.,0.);
10   Standard_Real R=0.;
11   IS >> P >> A >> AX >> AY >> R;
12   C = new Geom_Circle(gp_Ax2(P,A,AX),R);
13   return IS;
14 }

 

Lambda表达式简介

4.1.3 <3D curve record 3>-Ellipse

示例: 

金沙国际网址 24

BNF定义: 

金沙国际网址 25

详细说明: 

<3D curve record 3>定义了椭圆。椭圆的数据包含三维点P,三维正交坐标系N、Dmaj、Dmin和两个非负实数rmaj和rmin,且rmin<=rmaj。椭圆位于中心点P,法向量为N的平面上,且长轴、短轴的方向分别为Dmaj, Dmin,长轴、短轴上的半径分别为rmaj, rmin。椭圆的参数方程定义如下所示: 

金沙国际网址 26

示例数据表示的椭圆的中心点P=(1,2,3),平面的法向量N=(0,0,1),长轴方向Dmaj=(1,0,-0),短轴方向Dmin=(-0,1,0),长轴半径为5,短轴半径为4, 

金沙国际网址 27

读取椭圆部分的程序代码如下所示:

1 //=======================================================================
 2 //function : ReadCurve
 3 //purpose  : 
 4 //=======================================================================
 5 static Standard_IStream& operator>>(Standard_IStream& IS,
 6                     Handle(Geom_Ellipse)& E)
 7 {
 8   gp_Pnt P(0.,0.,0.);
 9   gp_Dir A(1.,0.,0.),AX(1.,0.,0.),AY(1.,0.,0.);
10   Standard_Real R1=0.,R2=0.;
11   IS >> P >> A >> AX >> AY >> R1 >> R2;
12   E = new Geom_Ellipse(gp_Ax2(P,A,AX),R1,R2);
13   return IS;
14 }

 

 

为什么Lambda表达式比匿名方法更好?

4.1.4 <3D curve record 4>-Parabola

示例: 

金沙国际网址 28

BNF定义: 

金沙国际网址 29

详细说明: 

<3D curve record 4>定义了抛物线。抛物线数据包含三维点P,三维正交坐标系坐标轴方向N,Dx,Dy和一个非负的实数f。抛物线通过点P,且位于法向量为N的平面上,焦点长度为f,其参数方程如下所示: 

金沙国际网址 30

示例数据表示的抛物线过点P=(1,2,3),位于平面的法向N=(0,0,1),抛物线的另两个轴方向Dx=(1,0,-0),Dy=(-0,1,0),焦点长度f=16。参数方程为: 

金沙国际网址 31

读取抛物线部分的程序代码如下所示:

 1 //=======================================================================
 2 //function : ReadCurve
 3 //purpose  : 
 4 //=======================================================================
 5 static Standard_IStream& operator>>(Standard_IStream& IS,
 6                     Handle(Geom_Parabola)& C)
 7 {
 8   gp_Pnt P(0.,0.,0.);
 9   gp_Dir A(1.,0.,0.),AX(1.,0.,0.),AY(1.,0.,0.);
10   Standard_Real R1=0.;
11   IS >> P >> A >> AX >> AY >> R1;
12   C = new Geom_Parabola(gp_Ax2(P,A,AX),R1);
13   return IS;
14 }

 

 

C# 2.0的匿名方法允许我们以内联的方式来实现委托实例,而C# 3.0的Lambda表达式运行我们使用一种更接近人的思维,更自然的方式来实现类似于匿名方法同样的效果。

4.1.5 <3D curve record 5>-Hyperbola

示例: 

金沙国际网址 32

BNF定义: 

金沙国际网址 33

详细说明: 

<3D curve record 5>定义了双曲线。双曲线定义数据有三维点P,三维正交坐标系坐标轴方向为N,Dx,Dy和两个非负实数Kx,Ky。双曲线过P点且法向量为N的平面上,其参数方程如下所示: 

金沙国际网址 34

示例数据表示的双曲线过点P=(1,2,3)且位于的平面的法向N=(0,0,1),其它的数据Dx=(1,0,-0),Dy=(-0,1,0),Kx=5和Ky=4。其参数方程为: 

金沙国际网址 35

读取双曲线部分的程序代码如下所示:

 1 //=======================================================================
 2 //function : ReadCurve
 3 //purpose  : 
 4 //=======================================================================
 5 static Standard_IStream& operator>>(Standard_IStream& IS,
 6                     Handle(Geom_Hyperbola)& H)
 7 {
 8   gp_Pnt P(0.,0.,0.);
 9   gp_Dir A(1.,0.,0.),AX(1.,0.,0.),AY(1.,0.,0.);
10   Standard_Real R1=0.,R2=0.;
11   IS >> P >> A >> AX >> AY >> R1 >> R2;
12   H = new Geom_Hyperbola(gp_Ax2(P,A,AX),R1,R2);
13   return IS;
14 }

 

 

Lambda表达式格式

4.1.6 <3D curve record 6>-Bezier Curve

示例: 

金沙国际网址 36

BNF定义: 

金沙国际网址 37

详细说明: 

<3D curve record 6>定义了Bezier曲线。Bezier曲线数据包含有理标志r,曲线的次数m(degree m <= 25查看源代码可知OpenCascade可处理的B样条次数不超过25)和带权的控制点(weight poles)。当有理标志位r=0时,weight poles就是m+1个三维点:B0,B1...Bn;当有理标志位r=1时,weight poles就是带权的控制点B0 h0... Bm hm。Bi是三维点,hi是[0,m]正实数,即权因子。当有理标志位r=0时,即不是有理Bezier曲线时,hi=1。Bezier曲线参数方程如下所示: 

金沙国际网址 38

示例数据表示的Bezier曲线是有理Bezier曲线,因其有理标志位r=1,次数m=2,带权控制点及权因子分别为:B0=(0,1,0),h0=4,B1=(1,-2,0),h1=5,B2=(2,3,0),h2=6。Bezier曲线的参数方程如下所示: 

金沙国际网址 39

读取Bezier曲线部分的程序代码如下所示:

 1 //=======================================================================
 2 //function : ReadCurve
 3 //purpose  : 
 4 //=======================================================================
 5 static Standard_IStream& operator>>(Standard_IStream& IS,
 6                     Handle(Geom_BezierCurve)& B)
 7 {
 8   Standard_Boolean rational=Standard_False;
 9   IS >> rational;
10 
11   // poles and weights
12   Standard_Integer i=0,degree=0;
13   IS >> degree;
14 
15   TColgp_Array1OfPnt poles(1,degree+1);
16   TColStd_Array1OfReal weights(1,degree+1);
17   
18   for (i = 1; i <= degree+1; i++) {
19     IS >> poles(i);
20     if (rational)
21       IS >> weights(i);
22   }
23 
24   if (rational)
25     B = new Geom_BezierCurve(poles,weights);
26   else
27     B = new Geom_BezierCurve(poles);
28 
29   return IS;
30 }

 

(参数列表)=>表达式或者语句块

4.1.7 <3D curve record 7>-B-Spline curve

示例: 

金沙国际网址 40

BNF定义: 

金沙国际网址 41

详细说明: 

<3D curve record 7>定义了B-Spline曲线。B-Spline曲线包含了有理标志位r,曲线次数m<=25,控制点数n>=2,重节点数k,带权控制点wieght poles和重节点multiplicity knots。 

当有理标志位r=0时,是非有理B样条曲线,weight poles有n个三维点B1,...,Bn;当有理标志位r=1时,是有理B样条曲线,weight poles是n个带权控制点对:B1, h1, .... Bn, hn。这里Bi表示一个三维点,hi表示一个[0,1]正实数。当有理标志位r=0时,hi=1。 

重节点有k对u1, q1, ... uk, qk。这里ui是重复度为qi>=1的节点。 

金沙国际网址 42

B-Spline曲线的参数方程如下所示: 

金沙国际网址 43

其中Ni,j有如下的递归定义: 

金沙国际网址 44

金沙国际网址 45

示例数据表示的B样条曲线为:有理标志位r=1,次数m=1,控制点数n=3,重节点数k=5,带权控制点:B1=(0,1,0),h1=4,B2=(1,-2,0),h2=5,B3=(2,3,0),h3=6;重节点u1=0,q1=1,u2=0.25,q2=1,u3=0.5,q3=1,u4=0.75,q4=1,u5=1,q5=1。B-Spline曲线的参数方程如下所示: 

金沙国际网址 46

读取B-Spline曲线部分的程序代码如下所示: 

 

 1 //=======================================================================
 2 //function : ReadCurve
 3 //purpose  : 
 4 //=======================================================================
 5 static Standard_IStream& operator>>(Standard_IStream& IS,
 6                     Handle(Geom_BSplineCurve)& B)
 7 {
 8 
 9   Standard_Boolean rational=Standard_False,periodic=Standard_False;
10   IS >> rational >> periodic;
11 
12   // poles and weights
13   Standard_Integer i=0,degree=0,nbpoles=0,nbknots=0;
14   IS >> degree >> nbpoles >> nbknots;
15 
16   TColgp_Array1OfPnt poles(1,nbpoles);
17   TColStd_Array1OfReal weights(1,nbpoles);
18   
19   for (i = 1; i <= nbpoles; i++) {
20     IS >> poles(i);
21     if (rational)
22       IS >> weights(i);
23   }
24 
25   TColStd_Array1OfReal knots(1,nbknots);
26   TColStd_Array1OfInteger mults(1,nbknots);
27 
28   for (i = 1; i <= nbknots; i++) {
29     IS >> knots(i) >> mults(i);
30   }
31 
32   if (rational)
33     B = new Geom_BSplineCurve(poles,weights,knots,mults,degree,periodic);
34   else
35     B = new Geom_BSplineCurve(poles,knots,mults,degree,periodic);
36   
37   return IS;
38 }

 

可以有多个参数,一个参数,或者无参数。参数类型可以隐式或者显式。例如:

4.1.8 <3D curve record 8>-Trimmed Curve

示例: 

金沙国际网址 47

BNF定义: 

金沙国际网址 48

详细说明: 

<3D curve record 8>定义了裁剪曲线(trimmed curve)。裁剪曲线数据包含:两个实数umin,umax和<3D curve record>,且umin<umax。裁剪曲线是将<3D curve record>描述的曲线B限制在[umin,umax]。裁剪曲线的参数方程如下所示: 

金沙国际网址 49

示例数据表示的裁剪曲线为:umin=-4,umax=5,曲线B(u)=(1,2,3)+u(1,0,0)。裁剪曲线的参数方程如下所示: 

金沙国际网址 50

读取裁剪曲线部分的程序代码如下所示: 

 

1 //=======================================================================
 2 //function : ReadCurve
 3 //purpose  : 
 4 //=======================================================================
 5 
 6 static Standard_IStream& operator>>(Standard_IStream& IS,
 7                     Handle(Geom_TrimmedCurve)& C)
 8 {
 9   Standard_Real p1=0.,p2=0.;
10   IS >> p1 >> p2;
11   Handle(Geom_Curve) BC;
12   GeomTools_CurveSet::ReadCurve(IS,BC);
13   C = new Geom_TrimmedCurve(BC,p1,p2);
14   return IS;
15 }

 

(x,y)=>x*y //多参数,隐式类型=>表达式
x=>x*10 //单参数,隐式类型=>表达式
x=>{return x*10} //单参数,隐式类型=>语句块
(int x) => x*10 //单参数,显式类型=>表达式
(int x) =>{return x*10} //单参数,显示类型=>语句块
()=>{Console.WriteLine()}//无参数

4.1.9 <3D curve record 9>-Offset Curve

示例: 

金沙国际网址 51

BNF定义: 

金沙国际网址 52

详细说明: 

<3D curve record 9>定义了偏移曲线(offset curve)。偏移曲线的数据包含偏移距离d,偏移方向D和曲线数据<3D curve record>。偏移曲线是将<3D curve record>描述的曲线沿矢量金沙国际网址 53偏移距离d后的结果。偏移曲线的参数方程如下所示: 

金沙国际网址 54

示例数据表示的偏移曲线为偏移距离d=2,方向D=(0,1,0),基曲线B(u)=(1,2,3)+u(1,0,0),其参数方程如下所示: 

金沙国际网址 55

读取偏移曲线部分程序代码如下所示:

1 //=======================================================================
 2 //function : ReadCurve
 3 //purpose  : 
 4 //=======================================================================
 5 static Standard_IStream& operator>>(Standard_IStream& IS,
 6                     Handle(Geom_OffsetCurve)& C)
 7 {
 8   Standard_Real p=0.;
 9   IS >> p;
10   gp_Dir D(1.,0.,0.);
11   IS >> D;
12   Handle(Geom_Curve) BC;
13   GeomTools_CurveSet::ReadCurve(IS,BC);
14   C = new Geom_OffsetCurve(BC,p,D);
15   return IS;
16 }

 

未完,待续……

 

注意:

1.Lambda表达式的参数类型可以省略,因为可以根据使用上下文进行推测。

2.Lambda表达式的主体(body)可以是表达式,也可以是语句块。

3.Lambda表达式传入的实参将参与类型推断,以及方法重载辨析。

4.Lambda表达式表达式和表达式体可以被转换成表达式树。

 

Lambda表达式与委托类型

Lambda表达式L可以被转换成委托类型D,需要满足以下条件:

1.L和D拥有相同的参数个数

2.L参数类型要与D参数类型相同。注意隐式类型要参与类型辨析。

3.D的返回类型与L相同,无论L是表达式,还是语句块。

 

 

本文由金沙国际网址发布于摄影,转载请注明出处:实际上Lambda表达式的本质是匿名方法,"匿名方法

关键词:

上一篇:金钟 CX-440 三脚架

下一篇:金沙国际网址int()函数的第二个参数是转换进制,