拉姆达表达式的制作方法

文档序号:6568398阅读:500来源:国知局
专利名称:拉姆达表达式的制作方法
拉姆达表达式背景编程语言持续发展以便于程序员的说明以及高效的执行。在早期的计算机语 言中,低级机器码是普遍的。采用机器码,计算机程序或构成计算机程序的指令是 用机器语言或汇编语言来编写的,并由硬件(例如,微处理器)执行。这些语言提 供了控制计算硬件的有效手段,但是对程序员而言非常难以理解和开发复杂逻辑。 随后,引入了提供各层抽象的语言。因此,程序员可用较高级源语言在较高的级别 上编写程序,该程序然后经由编译器或解释器转换成硬件理解的较低级机器语言。 编程中的进一步进展提供了另外的抽象层,以允许比以前快得多地指定更高级的编 程逻辑。然而,这些进步并不是在没有处理成本的情况下发生的。编译器和/或解释器承担了将高级逻辑转换成可执行机器码的负担。一般而言,编译器和/或解释器是接收以源编程语言(例如,C、 C#、 Visual Basic、 Java…)指定的程序并将由此提供的逻辑转换成可由硬件设备执行的机器语言的组件。然而, 转换无需逐字完成。实际上,常规的编译器和/或解释器分析源代码并生成非常高 效的代码。例如,程序员编写直观且为人们易于理解的陈述了操作逻辑流程的代码, 但是这通常对计算机执行而言是低效的。编译器和/或解释器可标识该低效性并通 过消除不必要的操作和/或重新排列指令的执行同时仍实现预期的结果而在硬件级 上改进程序性能。以此方式,程序员可创建健壮且高效的软件程序。概述以下提出了简化概述以提供对所要求保护的主题的某些方面的基本理解。本 概述并不是广泛综述。它并不旨在标识所要求保护的主题的关键/决定性要素,也 不旨在描绘其范围。其唯一的目的是以简化形式提出某些概念作为以后提出的更详 细描述的序言。简言之,所提供的主题涉及拉姆达表达式以及这种表达式在命令性和/或面向 对象的计算机编程语言中的应用。拉姆达表达式提供了比常规形式更简明的指定值 或数据的方式。例如,拉姆达表达式可提供指定匿名方法的更简洁且功能性方式。拉姆达表达式也可参与类型推导。拉姆达表达式本身无需具有类型。相反, 它可具有参数类型和返回类型等等。为减少与拉姆达表达式的指定相关联的冗长性,这些类型不必明确规定。提供了用于基于上下文来推导这些类型的系统和方法。 另外,拉姆达表达式可便于重载解决。例如,在一方法被重载的情况中,拉姆达表达式可便于选择一特定方法。例如,在将拉姆达表达式用作重载方法的自变量时,该表达式的返回类型可用作选择特定方法的因素。为实现上述和相关目的,此处结合以下描述和附图描述了所要求保护的主题的某些说明性方面。这些方面指示了可实施本主题的各种方式,所有这些都旨在落入所要求保护的主题的范围之内。当结合附图考虑以下详细描述时,其它优点和新颖特征将变得显而易见。附图简述

图1是用于拉姆达表达式的执行系统的框图。图2是用于支持拉姆达表达式的编程系统的框图。图3是类型转换系统的框图。图4是推导与拉姆达表达式相关联的类型的类型推导系统的框图。图5是对拉姆达表达式进行类型检查的类型检查系统的框图。图6是重载解决系统的框图。图7是拉姆达表达式执行方法的流程图。图8是编程方法的流程图。图9是拉姆达表达式转换方法的流程图。图IO是与拉姆达表达式相关联的类型推导方法的流程图。图11是示例性类型推导方法的流程图。图12是重载解决方法的流程图。图13是编译环境的示意性框图。图14是示出合适的操作环境的示意性框图。图15是示例计算环境的示意性框图。详细描述现在参考附图来描述本发明的各方面,在全部附图中相同的标号指代相同或 对应的元素。然而,应当理解,附图和所涉及的详细描述并不旨在将所要求保护的主题限于所公开的具体形式。相反,本发明覆盖落入所要求保护的主题的精神和范 围内的所有修改、等效技术方案和替换方案。如此处所使用的,术语"组件"、"系统"等指的是计算机相关的实体,它 或者是硬件、硬件和软件的组合、软件、或者是执行中的软件。例如,组件可以是, 但不限于,运行在处理器上的进程、处理器、对象、可执行代码、执行的线程、程 序和/或计算机。作为说明,运行在计算机上的应用程序和计算机都可以是组件。 一个或多个组件可驻留在一个进程和/或执行的线程内,并且组件可位于一台计算 机上和/或分布在两台或多台计算机之间。此处所使用的词语"示例性"意味着用作示例、实例或说明。此处被描述为 "示例性"的任何方面或设计不一定被解释为对于其它方面或设计是优选或有利 的。此外,所公开的主题可使用产生控制基于计算机或处理器的设备来实现此处 详细描述的各方面的标准编程和/或工程技术而被实现为系统、方法、装置或制品。 如此处所使用的术语"制品"(或者替代地,"计算机程序产品")旨在涵盖可从 任何计算机可读设备、载体或介质访问的计算机程序。例如,计算机可读介质可包 括但不限于,磁存储设备(例如,硬盘、软盘、磁条……)、光盘(例如,紧致盘(CD)、数字多功能盘(DVD)……)、智能卡、以及闪存设备(例如,卡、棒)。另外,应当理解,可采用载波来承载诸如在发送和接收邮件或访问诸如因特网或局域网(LAN)等网络时使用的计算机可读电子数据。当然,本领域的技术人员将 认识到,可对此配置进行许多修改而不脱离所要求保护的主题的范围或精神。一开始转向图l,示出了用于执行拉姆达表达式的执行系统100。系统100包 括接收器组件110和求值组件120。接收器组件110获得拉姆达表达式并将该表达 式发送到求值组件120。求值组件120获得该表达式,计算并返回特定类型的值。拉姆达表达式是一种简洁地指定或捕捉值和数据等的机制。在一个实例中, 可采用拉姆达表达式来指定或替换匿名方法。匿名方法允许与委托(delegate)相 关联的程序性代码例如被"内联地"表达或编写。作为示例,假定希望对以C弁表 达为ffinumerable<customer>cs的顾客集合进行查询。返回低于35岁的顾客的查询 将被指定为<formula>formula see original document page 6</formula>static bool f(customer c) {return cage < 35;}可替代地指定匿名方法以简化这一表达式的指定,例如 cs.where(delegate (Customer c) {return cage <35;})这比先前的示例更简洁,至少是因为不需要指定新类。相反,由delegate关键字标 识的匿名方法被内联为自变量。然而,对匿名方法的指定仍是相当冗长的。此处, 可采用拉姆达表达式来替换匿名方法。根据上一示例,可利用拉姆达表达式来更简 洁且更功能性地指定信息。具体地,拉姆达表达式可对应于cs.where(|c| c,age<35)此处,标识了参数"c"以及表达式"c.age<35"。这远不如先前的表示那样冗长, 至少是因为它消除了不必要的句法以及显式类型声明(例如,Customer)。拉姆达表达式可被指定为由竖线括住的参数列表后跟表达式或表达式主体。 例如,拉姆达表达式可如伪BNF (巴科斯-诺尔范式)中指定的结构化拉姆达表达式l拉姆达参数列表wffisl拉姆达表达式主体 拉姆达参数列表显式类型化拉姆达参数列表 隐式类型化拉姆达参数列表 显式类型化拉姆达参数列表 显式类型化拉姆达参数显式类型化拉姆达参数列表,显式类型化拉姆达参数显式类型化拉姆达参数类型标识符 隐式类型化拉姆达参数列表隐式类型化拉姆达参数隐式类型化拉姆达参数列表,隐式类型化拉姆达参数 隐式类型化拉姆达参数标识符 拉姆达表达式主体表达式拉姆达表达式可具有通式l参数l表达式。然而,也可构想另外的指定方式。例 如,拉姆达表达式可用参数=>表达式、(参数)=>表达式以及其它定界符和/或符号 的组合或排列的形式来声明。句法也可在拉姆达表达式具有空参数列表的情况下有 所不同。在竖线对表达式的参数进行定界并且存在空参数列表的情况下,所得的语 言符号将是相邻的竖线字符(例如,||或| I)。这可产生关于用于在某些编程语言中表示条件"OR"(或)的相邻的竖线的歧义。因此,可釆用替换表示,诸如但不限于,相邻的竖线后跟等号(例如,I 1=)。然而,应当理解,相邻的竖线字符可被重载并用于表示空参数列表以及条件"OR"。"l参溯表这式"形式的拉姆达表达式可对应于"delegate(##){return表这 _^;}"形式的匿名方法。因此,拉姆达表达式可替换较冗长的匿名方法。然而,在 拉姆达表达式与匿名方法之间存在差别。特别地,拉姆达表达式允许省略并推导参 数类型(如下文将详细描述的),而匿名方法要求显式规定参数类型。另外,匿名 方法的主体被写作一语句块,而拉姆达表达式的主体可以或者是表达式或者是语句 块。回到图1,接收器组件110接收、检索或以其它方式获得上述拉姆达表达式或 其表示。然后可将拉姆达表达式发送给求值组件120。求值组件120计算与该拉姆 达表达式相关联的值并返回这一值。作为示例而非局限,系统100可以与一程序执 行系统相关联。拉姆达表达式可以是程序语句或诸如自变量等其它构造的一部分, 而返回的值可以进一步用于处理相关联的语句。此外,接收器组件110和求值组件 120可协作以允许对嵌套和/或分层的拉姆达表达式进行求值。例如cs.where(|c| c.age<35).Select(|c| new{name=c.name, age=c.age})该语句表示选择顾客名字和年龄(Select(|c| new{name=c.name, age=c.age})),并且 从中标识其年龄小于35岁的集合顾客(cs) (cs.where(|c|c.age<35))。图2描绘了用于拉姆达表达式的编程系统或环境200。系统200包括开发组件 210、编译器组件220和可执行程序230。开发组件210从诸如程序员等用户接收 输入。输入可包括含有至少一个拉姆达表达式的多个程序性语句或构造。例如,语 句可包括拉姆达表达式作为自变量或将其赋值给一函数或类型。表达式可用多种形 式中的任一种来表示,这些形式包括但不限于利用相邻的竖线(例如,|c| c.age<35) 或双箭头(例如,c=>C.age)来将参数与表达式主体分离。开发组件210可对应于代码编辑器(例如,文本或图形的)、集成开发环境(IDE)等。编译器组件220 (以下更详细描述)可从开发组件210接收程序性源代码,并生成计算机可执行程 序230或替换地可在运行时例如由运行时(JIT)编译器进一步编译的某种中间格 式(例如,IL (中间语言))。编译器组件220也可以是IDE的一部分。图3描绘了类型转换系统300。拉姆达表达式可被分类为具有特殊转换规则的 值。拉姆达表达式没有类型,但可被转换(例如,隐式地)为诸如委托类型等兼容 类型。系统300包括兼容性组件310和转换组件320。这两个组件都可位于诸如图 2的编译器220等编译器内。兼容性组件310接收例如由拉姆达表达式和委托类型 函数组成的拉姆达表达式赋值。随后,兼容性组件可关于某些规则或条件312来分 析赋值类型以确定赋值类型是否与拉姆达表达式兼容。如果不满足条件312,则所 赋的类型不兼容,并且可由兼容性组件310生成错误或异常。或者,如果满足条件 310并且类型与拉姆达表达式兼容,则该表达式可由转换组件320转换成该类型。 作为示例而非局限,当满足各种条件312时,委托类型D可与拉姆达表达式 L兼容。例如,D可具有非空(non-void)返回类型以及无引用(no ref)或传出(out) 参数。除此之外或作为替代,D和L可能必须具有相同个数的参数,并且如果L 具有显式类型化参数列表,则D中的每一参数的类型必须与L中相应参数的类型 相同。除此之外或作为替代,当L的参数被给予D中相应参数的类型时,L的表 达式主体可以是可被隐式地转换成D的返回类型的有效表达式。作为示例,假定 表示取类型A的自变量并返回类型R的值的泛型委托类型Function<A, R>:delegate R Function<A,R>(A, arg); 此外,假定以下赋值Func<int, int> f = |x| x +1;拉姆达表达式lxl x +1可由转换组件320成功地转换为委托类型Func<int, int>,因为当x被给予int类型时,x+1是可隐式地转换成int类型的有效表达 式。赋值等效于更冗长的匿名方法Function<int, int> f = delegate(intx) {returnx+1};应当注意,拉姆达表达式允许但不要求推导x的类型,而匿名方法要求显式地规定类型。转向图4,描绘了允许关于拉姆达表达式进行推导的类型推导系统400。类型 推导系统400可以是诸如编译器组件220 (图2)等编译器的一部分。类型推导系 统400包括上下文组件410和推导组件420。上下文组件410在接收或标识了拉姆 达表达式之后,例如从从中获得特定拉姆达表达式的程序接收、检索或以其它方式 获得与该拉姆达表达式有关的上下文信息。这一上下文信息可以是与拉姆达表达式 有关的任何信息,包括语句、表达式、类、类型等。上下文信息由推导组件420 例如经由数据包的传输从上下文组件410中获得。此外,拉姆达表达式可被提供给 推导组件420以便于与其相关联的类型的类型推导。推导组件420随后可利用该上 下文信息来推导诸如拉姆达表达式参数、表达式主体和/或返回类型的类型等类型。 当然,类型推导可被传播,使得所推导的类型可帮助推导其它元素的类型。因此, 推导组件420可生成并维护可用于进行另外的推导的推导集430。应当理解,拉姆达表达式本身没有诸如结构类型等与其相关联的类型。相反, 参数和表达式主体或返回值具有相关联的类型。认识到结构类型的推导对于面向对 象的语言而言并非是平凡的。考虑以下示例性拉姆达表达式和定义对象的类var f = (|x| x.age) class Customer{int age} class Wine{string age}不能通过简单地分析该拉姆达表达式以及customer (顾客)和wine (酒)对象类 的结构来确定参数(x)和返回表达式值(x.age)的类型。参数可具有类型customer, 这对于age (年龄)返回整型类型。或者,参数可以是对age返回string (串)类 型的wine类型。然而,类型可从上下文中推导。考虑以下声明delegate R Func <A, R> (A arg) Func<Customer, bool> f 二 |x| x.age < 35;指定了取A并返回R的委托类型。拉姆达表达式然后被赋值给委托函数。从这一 附加的上下文信息中,可推导类型。基于拉姆达表达式对函数的赋值,推导组件 420可确定自变量S必须为Customer。推导组件420然后可推导参数x是Customer 类型。从这一信息中,可确定x.age为来自Customer类或对象定义的整型类型。随 后,例如经由验证组件422通过比较返回类型来验证类型推导。此处,委托函数Func返回布尔类型(bool),并且拉姆达表达式通过年龄是否小于35的判断来返 回布尔类型。因此,推导组件能够基于上下文成功地推导与拉姆达表达式相关联的 类型。实际上,拉姆达表达式向委托函数的赋值允许将上下文和类型信息下压到拉 姆达表达式。为便于涉及拉姆达表达式的类型推导的清楚和理解,将描述另一示例性情形。 考虑以下0#中的类定义Namespace System.Querypublic static class Sequencepublic static IEnumerable<S〉 Sdect<T,S>(this IEnumerable<T> source, Func<T, S> selector)foreach (T element in source) yield return selector (element);假定例如用using子句导入了 System.Query名字空间,并给出了具有类型为string 的Name (名字)属性的类Customer,则可采用Select (选择)方法来从顾客列表 中选择名字。例如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〉类型。以下示例示出了拉姆达类型推导如何允许类型信息在泛型方法调用中的自变 量之间"流动"。假定以下方法及其调用Static Z F<X, Y, X>(X value, Func〈X, Y> fl, Func<Y, Z> f2 { return f2(fl (value));double seconds = F("l: 15:30", |s| TimeSpan.Parse(s), |t| t.TotalSeconds);对于调用的类型推导首先以与value (值)参数有关的自变量"1:15:30"开始进行。 由此,X可基于该自变量被推导为string。接着,对第一拉姆达表达式的参数s给 予推导的类型string。表达式TimeSpan.Parse(s)与fl的返回类型(Y)有关,因此 这可被推导为System.TimeSpan。最后,第二拉姆达表达式的参数t被给予推导的 类型System.TimeSpan,并且表达式t.TotalSeconds与f2的返回类型有关,从而使 得Z被推导为基于System.TimeSpan类的double (双精度)类型(未示出)。因此, 该调用的结果为double类型。转向图5,示出了支持对拉姆达表达式进行类型检查的类型检査系统500。类 似于图4的系统400,系统500可形成编译器的一部分。类型检查系统500包括上 下文组件410、推导组件420以及类型检查组件510。上下文组件410获得或标识 拉姆达表达式并例如从包括该拉姆达表达式的程序中检索与拉姆达表达式有关的 上下文信息。这一上下文信息可以是与拉姆达表达式直接或间接相关联的任何语 句、表达式、类、类型等。推导组件420接收该上下文信息以及要检查的拉姆达表 达式。推导组件420可利用由上下文组件410提供的上下文信息以及推导集430 中的其它推导来推导例如拉姆达表达式参数、表达式、其元素和/或返回类型的类 型。尽管出于减少冗余度的目的,类型可从拉姆达表达式中省略,但这些类型也可 被显式地声明。在这一情形中,类型检查组件510可将预期的或推导的类型与该显 式类型进行比较以确定它们是否匹配。更具体地,类型检查组件510可从推导组件 420接收所推导的类型。此外,显式类型可从提供给类型检查组件510或与所推导 的类型一起传递的拉姆达表达式中获得。类型检查组件510可将该显式类型和推导 的类型进行比较。如果类型匹配,则不需要采取任何动作。或者,如果类型不匹配, 则类型组件可生成标出这一事实的错误或异常。图6示出了重载解决系统600。系统600可包括上下文组件410、推导组件420、 以及选择组件610。例如,自变量中的拉姆达表达式在某些情况下可影响重载解决。
上下文组件410可接收、检索或以其它方式获得与拉姆达表达式有关的上下文信 息。该上下文信息可被发送到推导组件420。推导组件420可利用上下文信息以及 一个或多个以往的推导430 (推导集)来推导例如与拉姆达表达式相关联的类型。这些推导的类型然后可被传递给选择组件610。选择组件610可采用该信息以在一方法被重载时来确定或选择该方法或委托类型。随后可将拉姆达表达式转换成例如 特定的委托类型。选择组件610可采用各种过程或协议来选择重载的方法或委托类型。例如, 给定拉姆达表达式L,如果委托类型Dl和委托类型D2具有相同的参数列表并且 从L的返回类型到Dl的返回类型的隐式转换是比从L的返回类型到D2的返回类 型的隐式转换更好的转换,则将L隐式转换成委托类型Dl是比将L隐式转换成委 托类型D2更好的转换。如果这些条件不为真,则没有一个转换是更好的。当然, 反之亦然。考虑以下示例class ItemList<T>: List<T〉public int Sum<T>(Func<T, int〉 selector) { int sum = 0;foreach (T item in this) sum +=selector(item); return sum;public double Sum<T> (Func<T, double> selector^ double sum = 0;foreach (T item in this) sum +=selector(item); return sum;ItemLisKT〉类具有两个Sum (求和)方法。每一方法取selector (选择器)自变量, 该自变量从列表项中提取值来相加。所提取的值可以或者是整型或者是双精度的, 并且所得的和同样或者是整型或者是双精度的。Sum方法可用于从订单(order) 的细节(detail)行列表中计算和,例如class Detailpublic int UnitCount; public int double UnitPrice} void ComputeSum()(ItemList<Detail> orderDetails = GetOrderDetails(...);int totalUnits = orderDetails.Sum(|d| d.UnitCount);double orderTotal = orderDetails.Sum(|d| d,UnitPrice * d.UnitCount);}..在orderDetails.Sum的第一个调用中,两个Sum方法都适用,因为拉姆达表达式|(1| d.UnitCount与Function〈Detail, int〉和Function<Detail, double〉都兼容。然而, 通过选择组件610的重载解决选取第一个Sum方法,因为到Function<Detail, int> 的转换要比到FunctioiKDetail,double〉的转换好。具体地,UnitCount是整型类型 在 orderDetails.Sum 的第二个调用 中 , 仅第二个Sum方法适用,因为拉姆达表 达式ldld.UnitPrice + d.UnitCount产生类型为双精度的值。由此,选择组件610将对 该调用选取第二个Sum方法。上述系统已相对于若干组件之间的交互来描述。应当理解,这些系统和组件 可包括其中指定的那些组件或子组件、所指定的组件或子组件中的某一些、和/或 另外的组件。例如,系统可包括上下文组件410、推导组件420、类型检査器组件 510以及选择组件610或其组合。子组件也可被实现为通信上耦合到其它组件而非 包括在父组件内的组件。另外,应当注意, 一个或多个组件可被组合成提供聚集功 能的单个组件或被划分成若干子组件。组件也可与此处未具体描述但本领域的技术 人员已知的一个或多个其它组件交互。此外,如将理解的,以上公开的系统和以下的方法的各部分可包括人工智能 或基于知识或规则的组件、子组件、进程、装置、方法或机制(例如,支持向量机、 神经网络、专家系统、贝叶斯信任网络、模糊逻辑、数据融合引擎、分类器……) 或由其组成。这些组件及其它组件可自动化所执行的某些机制或进程,以使得系统 和方法的各部分更适应以及更高效和智能。例如,推导组件420可利用人工智能、 机器学习或类似的机制来便于类型推导。除此之外或作为替代,选择组件610可采 用这些智能机制来便于重载解决。鉴于以上描述的示例性系统,将参考图7-12的流程图来更好地理解可根据所 公开的主题实现的方法。尽管出于解释简明的目的,方法被示出并描述为一系列框, 但是可以理解和明白,所要求保护的主题不受框的顺序的限制,因为根据此处所描 绘和描述的,某些框可以按不同的顺序和/或与其它框同时发生。此外,并非所有 示出的框都是实现以下描述的方法所必需的。另外,还应当理解,以下以及在全部说明书中公开的方法能够被储存在一制 品上以便于将这些方法运送和传输到计算机。如此处所适用的术语制品旨在涵盖可 从任何计算机可读设备、载体或介质访问的计算机程序。转向图7,描绘了拉姆达表达式执行方法700。在参考标号710处,接收、检 索或标识拉姆达表达式。拉姆达表达式可包括零个或多个参数以及表达式或表达式 主体。参数可用多种方式从表达式定界。例如,拉姆达表达式参数可被包含在两条竖线、圆括号内,或通过利用双箭头等与表达式主体分离。作为示例,拉姆达表达 式可采用"|x|x+l" 、 "(x,y)x+l"或"X=>X+1"的形式。此外,可不同地,例如 使用"||="来指定没有参数的拉姆达表达式,以将其与条件or "II"清楚地区分。 在720处,计算拉姆达表达式的值,即对其求值。这一值可以是包括但不限于整型、 布尔和串的任何类型。在730处,返回所计算的值。例如,如果拉姆达表达式被指 定为自变量,则它可被返回到作出调用的语句或方法。或者,如果拉姆达表达式被 赋值给诸如委托等函数,则所计算的值可被返回到该函数。图8示出了与拉姆达表达式相关联的编程方法800的流程图。在参考标号810 处,指定拉姆达表达式。作为示例,拉姆达表达式可用代码编辑器和/或集成开发 环境来手动、自动或半自动指定。拉姆达表达式可如上所述包括零个或多个参数以 及表达式或表达式主体。拉姆达表达式可利用一个或多个符号来将参数从表达式定 界的任何数目的方式来指定。例如,拉姆达表达式可被表示为"|c| cage" 、 "c =>C.age"或"(c)c.age"。此外,拉姆达表达式可在计算机可执行程序内被指定为 自变量或赋值给函数等等。在标号820处,将包括拉姆达表达式的程序提供给编译 器以供编译。从源代码中,编译器可产生机器可执行代码或可在运行时例如由运行 时(JIT)编译器进一步编译的某种中间代码。在830处,由计算机执行由编译器 生成的代码。图9描绘了拉姆达表达式转换的方法900。类似于匿名方法表达式,拉姆达表 达式可被分类为具有特殊转换规则的值。该值没有类型,但可被转换成诸如委托类 型等其它兼容类型。在参考标号901处,接收、检索或以其它方式获得或标识拉姆 达表达式。本实例中所接收的拉姆达表达式将被赋值给一特定类型。例如delegate R Func<A,R>(A arg); Func<int, int〉 f叫xl x + 1;此处,该拉姆达表达式被赋值给一委托类型函数。在920处,确定该拉姆达 表达式是否兼容或可被转换成它被赋值到的类型。基于所赋值的类型,可能有需要 验证的若干条件。例如,为确定委托类型D是否与拉姆达表达式L兼容,可验证 以下条件(1) D具有非空返回类型以及无引用或传出参数;(2) D和L具有 相同个数的参数,且如果L具有显式类型化参数列表,则D中的每一参数的类型 必须与L中相应参数的类型相同;以及(3)当每一参数L被给予D中相应参数的 类型时,L的表达式主体是可隐式转换成D的返回类型的有效表达式。如果在920处不满足兼容性条件,则在930处,生成错误或异常以指示将拉姆达表达式转换成不兼容类型的尝试。如果在920处满足条件,则允许该转换,并且在940处转换该 拉姆达表达式。在以上示例中,该拉姆达表达式被成功转换成委托类型Func<int, int>,因为当x被给予类型int时,x+l是可被隐式转换成类型int的有效表达式。这一赋值等效于使用匿名方法的更冗长形式 Func〈int, int〉 f = delegate(int x) {return x + 1};图IO是用于拉姆达表达式的类型推导方法1000。在标号1010处,在程序内 标识拉姆达表达式。在1020处,获得直接或间接与该拉姆达表达式有关的上下文 信息。这一上下文信息可包括类、对象、函数、方法和类型等等。例如,如果一拉 姆达表达式被赋值给一函数,则该函数提供了上下文信息。如果该拉姆达表达式是 方法自变量,则该方法提供了上下文信息。在参考标号1030处,推导与该拉姆达 表达式相关联的类型。例如,拉姆达表达式参数可基于所获得的上下文信息来推导。 拉姆达表达式主体元素和/或返回类型可基于先前的推导和上下文信息来确定。作 为示例,考虑作为自变量传递给泛型方法的拉姆达表达式。类型推导首先可独立于 每一自变量进行。在这一初始阶段,从作为拉姆达表达式的自变量中没有推导出任 何内容。然而,在初始阶段之后,可使用迭代过程从拉姆达表达式中作出附加推导。 特别地,可利用来自初始阶段的上下文信息来便于与方法的返回类型以及类型推 导。这一过程可被重复,直到无法作出进一步的推导。图11描绘了一示例性类型推导方法1100,以进一步理解拉姆达表达式可如何 参与类型推导过程。方法IIOO涉及对拉姆达表达式作为方法自变量的使用。应当 理解,方法IIOO也可被修改成处理拉姆达表达式赋值及其其它使用。在参考标号 lllO处,在必要时对方法和非拉姆达表达式自变量推导类型。在1120处,将一拉 姆达表达式标识为方法自变量。随后,在1130处,推导诸如参数和表达式元素类 型等拉姆达表达式类型。推导可基于例如当前的推导集,包括与相关联的或作出调 用的方法和/或其其它自变量有关的那些推导。在1140处,进行检查以确定方法参 数返回类型是否与拉姆达表达式的返回类型相同。如果不是,则可能存在错误代码 指定或推导。因此,可在1150处生成错误并且该方法终止。如果返回类型相同, 则该方法可前进到1160,在那里确定是否存在从中不能作出任何推导的任何其它 拉姆达表达式。如果有,则在1120处标识它们,并且该方法从那里继续。如果没 有其它拉姆达表达式,则该方法终止。作为示例,考虑以下代码摘录中的Select扩展方法Namespace System.Querypublic static class Sequencepublic static IEnumerable<S> Select<T,S>(this IEnumerable<T> source, Func<T,S> selector){ 一 一foreach (source中的T元素)产生返回selector (元素);Select方法可用于通过以下代码选择一顾客列表List<Customer> customers = GetCustomerList(); IEnumerable<string〉 names = customers.Select(|c| c.Name);该对Select的扩展方法调用可通过将该调用重写(例如,由编译器)为诸如以下的 静态方法调用来处理IEnumerable<string> names = Sequence.Select(customers, |c| c.Name);由于以上代码摘录的类型自变量不是显式指定的,因此它们可被推导。由于存在非拉姆达表达式自变量"customers",因此该类型可被首先推导。此处"customers" 与source参数有关,并且为T类型。从该特定调用中,T可被推导为Customer类 型。拉姆达表达式参数c和表达式主体c.Name与Select方法的selector参数有关。 拉姆达表达式参数c对应于类型T, T已被推导为类型Customer,因此c是Customer 类型。表达式主体c.Name对应于参数S, S可被推导为具有类型string。从Customer 类型中,可发现类c.Name是诸如string的特定类型。由此,拉姆达表达式主体和 方法参数返回类型都可以是string类型。因此,类型推导成功完成。本调用等效于 以下显式类型化的方法调用Sequence.SelecKCustomer, string>(customers, |Customer c| c.Name);本示例示出了如何确定类型信息以及它如何从作出调用的方法及其非拉姆达 表达式自变量流入拉姆达表达式。转向图12,提供了描绘重载解决方法1200的流程图。更具体地,方法1200 例如示出了拉姆达表达式如何参与解决重载方法。在参考标号1210处,将拉姆达 表达式标识为例如方法自变量。在1220处,例如通过基于一推导集的推导来确定拉姆达表达式的返回类型。最后,在1230处,基于拉姆达表达式的返回类型选择一特定重载方法。作为示例,考虑以下包括返回不同结果的两个Sum函数的类Class List<T>int Sum (Func<T, int> f) {…} double Sum (Func<T, double〉 f{...}此处,Sum被重载。现在假定指定了以下代码-List<Product> ps ps.Sum (|p| p.Price)为确定要调用哪一 Sum并解决重载,必须确定拉姆达表达式的返回类型。此处, 指示ps是LisKProducP"类型。从这点中,可推导p是Product (产品)类型。然后, 基于Product类定义,可确定p.Price的类型。对该示例假定该定义揭示了 p.Price 是双精度(double)类型。现在已知指定的代码取Product并返回双精度。因此, 该代码是Func<Product, (101^16〉类型。从这一知识中,可选择第二个Sum。如果 Product类定义揭示p.Price是整型(int)类型,则该代码将是Func<Product, inP类 型。在这一情形中,尽管整型类型可被转换成双精度,但是选择第一个Sum函数, 因为它是更好的匹配。如可从这一示例中理解的,不必首先选择函数之一 (例如, 如对结构类型化所做的),因为基于拉姆达表达式返回类型,可选择正确的方法或 函数。图13是描绘可用于产生实现代码(例如,可执行代码、中间语言……)的编 译器环境1300的框图。编译器环境1300包括编译器220,它包括前端组件1320、 转换器组件1330、后端组件1340、错误检查器组件1350、符号表1360、解析树 1370以及状态1380。编译器220接受源代码作为输入,并产生实现代码作为输出。 输入可包括但不限于如此处所描述的定界的程序性表达式或限定标识符。编译器环 境的组件和模块之间的关系示出了主要数据流。出于清楚和简明的目的未示出其它 组件和关系。取决于实现,组件可被添加、省略、拆分成多个模块、与其它模块组 合、和/或模块的其它配置。编译器220可接受具有与一元素序列的处理相关联的源代码的文件作为输入。 源代码可包括拉姆达表达式和相关联的函数、方法和/或其它程序性构造。编译器 220可结合用于分析构造和生成或注入代码的一个或多个组件来处理源代码。前端组件1320读取源代码并对其执行词法分析。本质上,前端组件1320读取源代码中的一字符序列(例如,字母数字)并将其转换成指示常量、标识符、运 算符号、关键字和标点符号等句法元素或语言符号。转换器组件1330将语言符号解析成中间表示。例如,转换器组件1330可检 査语言符号的句法并将其归组为表达式或其它句法结构,后者进而被接合成语句 树。概念上,这些树形成了一解析树1370。此外,在适当时,转换器模块1330可 将列出源代码中使用的符号名和类型信息以及相关特性的条目放置到符号表1330 中。状态1380可用于编译器1310在处理所接收或检索到的源代码并形成解析树 1370的进度。例如,不同的状态值指示编译器1310处于类定义或函数的开始、刚 声明了类成员、或已完成了表达式的状态中。当编译器进展时,它不断地更新状态 1380。编译器220可部分或完全将状态1380展示给外部实体,后者然后向编译器 220提供输入。基于源代码中的构造或其它信号(或如果有机会另外识别),转换器组件1330 或另一组件可注入对应的代码以便于高效且正确的执行。被编码到转换器组件 1330或其它组件的规则指示必须要完成什么来实现所需功能,并标识将注入代码 或要执行其它操作的位置。注入的代码通常包括一个或多个位置处添加的语句、元 数据或其它元素,但是该术语也可包括改变、删除或以其它方式修改现有的源代码。 注入的代码可被储存在一个或多个模板或以某一其它形式储存。另外,应当理解, 可进行符号表操纵和解析树变换。基于符号表1360和解析树1370,后端组件1340可将中间表示转换成输出代 码。后端组件1340将中间表示转换成可在目标处理器中执行或可由其执行的指令、 对变量的存储器分配等等。输出代码可由真实处理器来执行,但是本发明也构想了 可由虚拟处理器执行的输出代码。此外,前端组件1320和后端组件1340可执行诸如代码优化等另外的功能, 并且可将所描述的操作作为单个阶段或在多个阶段中执行。编译器220的组件的各 个其它方面本质上是常规的,并且可用执行等效功能的组件来替代。另外,在处理 源代码期间的各个阶段,错误检査器组件1350可检查诸如词法结构中的错误、句 法错误甚至是语义错误等错误。在检测到错误时,检査器组件1350可中止编译并 生成指示错误的消息。为了对所公开的主题的各方面提供上下文,图14和15以及以下讨论旨在提供其中可实现所公开的主题的各方面的合适的环境的简要概括描述。尽管以上在运行于一台计算机和/或多台计算机上的计算机程序的计算机可执行指令的一般上下 文中描述了本主题,但是本领域的技术人员将认识到本发明也可结合其它程序模块 来实现。 一般而言,程序模块包括执行特定任务和/或实现特定抽象数据类型的例 程、程序、组件、数据结构等。此外,本领域的技术人员可以理解,本发明可用其 它计算机系统配置来实施,包括单处理器或多处理器计算机系统、小型计算设备、 大型机、以及个人计算机、手持式计算设备(例如,个人数字助理(PDA)、电话、 手表……)、基于微处理器或可编程消费或工业电子产品等等。所示的各方面也可 在其中任务由通过通信网络链接的远程处理设备来执行的分布式计算环境中实施。 然而,本发明的某些(如果不是全部)方面可在独立计算机上实施。在分布式计算 环境中,程序模块可以位于本地和远程存储器存储设备中。参考图14,用于实现此处所公开的各方面的示例性环境1410包括计算机1412 (例如,台式机、膝上型计算机、服务器、手持式设备、可编程消费或工业电子产 品……)。计算机1412包括处理单元1414、系统存储器1416和系统总线1418。 系统总线1418将包括但不限于系统存储器1416的系统组件耦合至处理单元1414。 处理单元1414可以是各种可用处理器的任一种。双微处理器和其它多处理器体系 结构也可用作处理单元1414。系统总线1418可以是若干种总线结构类型的任一种,包括存储器总线或存储 器控制器、外围总线或外部总线、和/或使用任意各类可用总线体系结构的局部总 线,这些体系结构包括但不限于,ll位总线、工业标准体系结构(ISA)、微通道 体系结构(MCA)、扩展ISA (EISA)、智能驱动电子设备(IDE) 、 VESA局部 总线(VLB)、外围部件互连(PCI),通用串行总线(USB)、高级图形端口 (AGP)、 个人计算机存储卡国际协会总线(PCMCIA)以及小型计算机系统接口 (SCSI)。系统存储器1416包括易失性存储器1420和非易失性存储器1422。基本输入/ 输出系统(BIOS)包括如在启动时帮助在计算机1412内的元件之间传输信息的基 本例程,通常储存在非易失性存储器1422中。作为说明而非局限,非易失性存储 器1422可包括只读存储器(ROM)、可编程ROM (PROM)、电可编程ROM (EPROM)、电可擦除ROM (EEPROM)或闪存。易失性存储器1420包括担当 外部高速缓冲存储器的随机存取存储器(RAM)。作为说明而非局限,RAM以许 多形式可用,如同步RAM(SRAM)、动态RAM(DRAM)、同步DRAM( SDRAM)、 双数据率SDRAM(DDR SDRAM)、增强SDRAM(ESDRAM)、同步链路(Synchlink)DRAM (SLDRAM)和直接存储器总线(Rambus) RAM (DRRAM)。计算机1412也包括可移动/不可移动、易失性/非易失性计算机存储介质。例 如,图14示出了盘存储1424。盘存储1424包括但不限于,诸如磁盘驱动器、软 盘驱动器、磁带驱动器、Jaz驱动器、Zip驱动器、LS-100驱动器、闪存卡或记忆 棒等设备。另外,盘存储1424可单独包括存储介质或与其它存储介质组合,其它 存储介质包括但不限于,诸如紧致盘ROM设备(CD-ROM) 、 CD可记录驱动器 (CD-R驱动器)、CD可重写驱动器(CD-RW驱动器)或数字多功能盘ROM驱 动器(DVD-ROM)等光盘驱动器。为便于盘存储设备1424连接到系统总线1418, 通常使用可移动或不可移动接口,如接口 1426。可以理解,图14描述了担当用户和合适的操作环境1410中描述的基本计算 机资源之间的中介的软件。这类软件包括操作系统1428。操作系统1428可储存在 盘存储1424中,它用于控制并分配计算机系统1412的资源。系统应用程序1430 利用操作系统1428通过储存在系统存储器1416或盘存储1424上的程序模块1432 和程序数据1434对资源的管理。可以理解,本发明可用各种操作系统或操作系统 的组合来实现。用户通过输入设备1436向计算机1412输入命令或信息。输入设备1436包括 但不限于,诸如鼠标、跟踪球、指示笔、触摸垫等定点设备、键盘、话筒、操纵杆、 游戏垫、圆盘式卫星天线、扫描仪、TV调谐卡、数码相机、数码摄像机、web摄 像头等等。这些和其它输入设备经由接口端口 1438通过系统总线1418连接到处理 单元1414。接口端口 1438包括,例如,串行端口、并行端口、游戏端口和通用串 行总线(USB)。输出设备1440使用与输入设备1436相同类型端口中的某一些。 由此,例如,USB端口可用于向计算机1412提供输入,并从计算机1412输出信 息到输出设备1440。提供了输出适配器1442以说明存在一些输出设备1440,如显 式器(例如,平板和CRT)、扬声器和打印机,以及需要特殊适配器的其它输出 设备1440。输出适配器1442包括,作为说明而非局限,提供输出设备1440和系 统总线1418之间的连接装置的显卡和声卡。应当注意,其它设备和/或设备的系统 提供了输入和输出能力,如远程计算机1444。计算机1412可以使用到一个或多个远程计算机,如远程计算机1444的逻辑 连接在网络化环境中操作。远程计算机1444可以是个人计算机、服务器、路由器、 网络PC、工作站、基于微处理器的电器、对等设备或其它常见的网络节点等等, 并通常包括相对于计算机1412所描述的许多或所有元件。为简明起见,仅对远程计算机1444示出了存储器存储设备1446。远程计算机1444通过网络接口 1448逻 辑上连接至计算机1412,然后通过通信连接1450物理地连接。网络接口 1448包 含诸如局域网(LAN)和广域网(WAN)等通信网络。LAN技术包括光纤分布式 数据接口 (FDDI)、铜缆分布式数据接口 (CDDI)、以太网/IEEE 802.3、令牌环 /IEEE 802.5等等。WAN技术包括但不限于,点对点链路、诸如综合业务数字网 (ISDN)及其变体等电路交换网络、分组交换网络以及数字用户线(DSL)。通信连接1450指用于将网络接口 1448连接到总线1418的硬件/软件。尽管为 说明的清晰起见,示出通信连接1450在计算机1412内,然而它也可以对计算机 1412是外部的。仅出于示例性的目的,连接到网络接口 1448所必需的硬件/软件包 括内部和外部技术,如包括常规电话级调制解调器、线缆调制解调器和DSL调制 解调器的调制解调器、ISDN适配器和以太网卡或组件。图15是了本发明可与其交互的示例计算环境1500的示意框图。系统1500包 括一个或多个客户机1510。客户机1510可以是硬件和/或软件(如,线程、进程、 计算设备)。系统1500也包括一个或多个服务器1530。因此,系统1500可对应 于两层客户机服务器模型或多层模型(例如,客户机、中间层服务器、数据服务器) 以及其它模型。服务器1530也可以是硬件和/或软件(如,线程、进程、计算设备)。 例如,服务器1530可容纳线程,以通过使用本发明执行变换。客户机1510和服务 器1530之间的一个可能的通信可以是适用于在两个或多个计算机进程之间传输的 数据分组的形式。系统1500包括可用于促进客户机1510和服务器1530之间的通 信的通信框架1550。客户机1510操作上连接至可用于储存对客户机1510本地的 信息的一个或多个客户机数据存储1560。类似地,服务器1530操作上连接至可用 于储存对服务器1530本地的信息的一个或多个服务器数据存储1540。上文所描述的包括所要求保护的主题的各方面的示例。当然,不可能为了描 述所要求保护的主题而描述组件或方法的每一可想象的组合,但是本领域的普通技 术人员可以认识到,所公开的主题的许多另外的组合和置换是可能的。因此,所公 开的主题旨在包含落入所附权利要求书的精神和范围中的所有这样的改变、修改和 变化。此外,就在说明书和权利要求书中使用术语"包括"、"具有"或"含有" 而言,这类术语旨在以与术语"包含"用作权利要求书中的过渡词语所解释的类似 的方式为包含性的。
权利要求
1.一种计算机程序编译系统,包括获得与拉姆达表达式有关的上下文信息的上下文组件;以及基于由所述上下文组件提供的上下文信息来推导与所述拉姆达表达式相关联的类型的推导组件。
2. 如权利要求1所述的系统,其特征在于,还包括便于指定包括零个或多个 隐式和/或显式类型化参数以及主体的拉姆达表达式的开发组件。
3. 如权利要求2所述的系统,其特征在于,所述拉姆达表达式采用以下形式 之一l参数l主体、||=主体、参数=>主体、(参数)=〉主体、参数==>主体、以及(参数)==>主体。
4. 如权利要求3所述的系统,其特征在于,所述主体包括表达式和语句块之
5. 如权利要求l所述的系统,其特征在于,所述上下文组件从所述拉姆达表 达式向其赋值的函数中检索上下文信息。
6. 如权利要求5所述的系统,其特征在于,所述推导组件包括将所述函数的 返回类型与所述拉姆达表达式的返回类型进行比较以验证所推导的类型的确认组 件。
7. 如权利要求6所述的系统,其特征在于,所述函数是委托类型和泛型委托 类型之一。
8. 如权利要求5所述的系统,其特征在于,所述上下文组件从拉姆达表达式 参数类中检索上下文信息。
9. 如权利要求l所述的系统,其特征在于,还包括将所推导的类型与一个或 多个显式类型进行比较并在所述显式类型不匹配所推导的类型时生成错误的类型 检查器组件。
10. 如权利要求1所述的系统,其特征在于,还包括在所述拉姆达表达式和 一重载方法的参数相同的情况下基于所述拉姆达表达式的返回类型选择所述方法 的选择组件。
11. 一种计算机实现的拉姆达表达式转换方法,包括 确定一拉姆达表达式的电子表示与一委托类型是否兼容;以及如果所述表达式和所述类型兼容,则将所述拉姆达表达式转换成所述委托类 型,否则生成错误。
12. 如权利要求ll所述的方法,其特征在于,确定兼容性包括分析所述委托 类型,如果所述委托类型具有空返回类型或者包括引用或传出参数,则所述拉姆达 表达式和所述委托类型不兼容。
13. 如权利要求12所述的方法,其特征在于,确定兼容性包括分析所述拉姆 达表达式和所述委托类型中每一个的参数,如果所述拉姆达表达式和所述委托类型 具有不同数目的参数和/或相应的参数是不同类型的,则所述拉姆达表达式和所述 委托类型不兼容。
14. 如权利要求13所述的方法,其特征在于,确定兼容性包括分析与所述拉 姆达表达式相关联的表达式主体,如果所述表达式主体与所述委托类型的返回类型 是相同类型或者可被转换成所述委托类型的返回类型,则所述拉姆达表达式和所述 委托类型兼容。
15. —种其上储存有用于执行如权利要求11所述的方法的计算机可执行指令 的计算机可读介质。
16. —种计算机实现的重载解决方法,包括 标识一方法自变量中的拉姆达表达式; 确定所述拉姆达表达式的返回类型;以及 基于所述返回类型选择所述重载方法之一。
17. 如权利要求16所述的方法,其特征在于,选择所述重载方法之一包括选 择与所述拉姆达表达式具有相同返回类型的方法。
18. 如权利要求16所述的方法,其特征在于,选择所述重载方法之一包括选 择具有与所述拉姆达表达式的返回类型兼容的返回类型的方法。
19. 如权利要求16所述的方法,其特征在于,确定所述返回类型包括获得与 所述拉姆达表达式有关的上下文信息并基于所述上下文信息推导所述返回类型。
20. —种其上储存有用于执行如权利要求16所述的方法的计算机可执行指令 的计算机可读介质。
全文摘要
本发明涉及拉姆达表达式以及这种表达式在命令性和/或面向对象的计算机编程语言中的应用。拉姆达表达式可结合方法(例如,自变量、赋值……)来使用以提供更简明且功能性的代码指定方式。此外,拉姆达表达式可参与类型推导和检查以及重载解决等等。
文档编号G06F9/45GK101233487SQ200680027768
公开日2008年7月30日 申请日期2006年6月23日 优先权日2005年7月29日
发明者A·赫杰斯伯格, D·C·库尔卡尼, D·F·布克斯, G·S·凯茨泽伯格, H·J·M·梅杰, L·伯洛格纳斯, M·J·瓦纳, P·A·豪拉姆 申请人:微软公司
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1