2010-01-30 72 views
35

在我的追求,了解非常奇怪的看着“=>”操作,我找到了一个好place to start,笔者非常简洁明了:Lambda for Dummies ....任何人,任何人?我想不会

parameters => expression 

没有人有关于理解lambda基本知识的任何提示,以便更容易“解密”更复杂的lambda语句?

例如:如果我给出类似(从answer I received here):

filenames.SelectMany(f => 
     Assembly.LoadFrom(f).GetCustomAttributes(typeof(PluginClassAttribute), true) 
     .Cast<PluginClassAttribute>() 
     .Select(a => a.PluginType) 
).ToList(); 

我怎么能去破坏这种分解成更简单的部分?


更新:想炫耀我的第一个lambda表达式。不要对我笑,但我做到了不复制别人的榜样......和它的工作在第一时间:

public ModuleData[] GetStartModules() 
{ return modules.FindAll(start => start.IsBatch == true).ToArray(); } 
+0

基本上,lambda表达式是像任何其他功能除了它们被定义,无论你需要他们。你熟悉匿名课程吗?他们只是为了方法而已。 (作为评论张贴,因为我不能希望接近其他答案的质量) – RCIX 2010-01-30 09:42:09

+0

我将无耻地插入一个乔恩Skeet参考:http://csharpindepth.com/Articles/Chapter5/Closures.aspx这实际上帮助增加了我对lambda表达式的理解。非常好的阅读,当然! – IAbstract 2010-02-08 22:53:48

+3

我强烈建议您访问Lambdaexpression.net – Delashmate 2011-09-16 09:33:22

回答

39

让我们来分析您的代码示例:

filenames.SelectMany(f => 
     Assembly.LoadFrom(f).GetCustomAttributes(typeof(PluginClassAttribute), true) 
     .Cast<PluginClassAttribute>() 
     .Select(a => a.PluginType) 
).ToList(); 

所以,我们开始与一个叫filenamesstring[]。我们调用阵列上的SelectMany扩展方法,然后我们对结果调用ToList

filenames.SelectMany(
    ... 
).ToList(); 

SelectMany需要委托作为参数,在这种情况下,代表必须采取类型string的一个参数作为输入,并返回IEnumerable<T>(其中推断出T的类型)。这是lambda表达式进入阶段:

filenames.SelectMany(f => 
     Assembly.LoadFrom(f).GetCustomAttributes(typeof(PluginClassAttribute), true) 
).ToList() 

什么会发生在这里是filenames阵列中的每个元素,委托将被调用。 f是输入参数,并且=>右侧的内容是代表所指的方法体。在这种情况下,将调用Assembly.LoadFrom作为数组中的文件名,使用参数f将文件名传入LoadFrom方法。在返回的AssemblyInstance上,将调用GetCustomAttributes(typeof(PluginClassAttribute), true),它将返回一个Attribute实例的数组。所以编译器不能推断出前面提到的T的类型是Assembly

在返回的IEnumerable<Attribute>上,将调用Cast<PluginClassAttribute>(),返回IEnumerable<PluginClassAttribute>

所以现在我们有一个IEnumerable<PluginClassAttribute>,我们调用Select就可以了。该Select方法类似于SelectMany,但返回代替IEnumerable<T>T类型(这是由编译器推断出)的单个实例。该设置是相同的;在IEnumerable<PluginClassAttribute>每个元素它将调用定义的委托,当前元素值传递到其中:

.Select(a => a.PluginType) 

再次,a是输入参数,a.PluginType是方法体。因此,对于列表中的每个PluginClassAttribute情况下,它会返回PluginType属性的值(我将承担此属性的类型Type的)。

摘要
如果我们的点点滴滴粘合在一起:

// process all strings in the filenames array 
filenames.SelectMany(f => 
     // get all Attributes of the type PluginClassAttribute from the assembly 
     // with the given file name 
     Assembly.LoadFrom(f).GetCustomAttributes(typeof(PluginClassAttribute), true) 
     // cast the returned instances to PluginClassAttribute 
     .Cast<PluginClassAttribute>() 
     // return the PluginType property from each PluginClassAttribute instance 
     .Select(a => a.PluginType) 
).ToList(); 

Lambda表达式与代表
让我们完成这个关闭的lambda表达式比较代表。看看下面的清单:

List<string> strings = new List<string> { "one", "two", "three" }; 

说,我们要过滤掉那些以字母“T”开头:

var result = strings.Where(s => s.StartsWith("t")); 

这是最常用的方法;使用lambda表达式进行设置。但也有备选方案:

Func<string,bool> func = delegate(string s) { return s.StartsWith("t");}; 
result = strings.Where(func); 

这基本上是同一件事:第一,我们创建类型Func<string, bool>的委托(这意味着它需要一个string作为输入参数,并返回一个bool)。然后我们将该委托作为参数传递给Where方法。这是编译器在第一个示例的幕后(strings.Where(s => s.StartsWith("t"));)为我们做的。

三分之一选择是简单地委托传递给非匿名方法:

private bool StringsStartingWithT(string s) 
{ 
    return s.StartsWith("t"); 
} 

// somewhere else in the code: 
result = strings.Where(StringsStartingWithT); 

所以,我们正在寻找在这里的情况,lambda表达式是定义一个相当简洁的方式委托,通常指的是匿名方法。

如果你有能读到这里所有的方式,好了,感谢您的时间:)

+0

@FredrikMörk:非常好的解释,并感谢您在“图表”中的帮助。当你不理解速记版本时,它是很吓人的。在你和Kastermester之间,我想我得到了这个...而且它不是火车!再次感谢你! – IAbstract 2010-01-30 16:37:12

+1

这是一个很好的回应,并回答了我们其他人似乎忽略的实际问题之一 - 很棒的工作! – kastermester 2010-01-31 19:48:36

+1

伟大的回应+1 – xandercoded 2010-04-08 03:25:40

0

Lambda calculus是许多编程语言中常见的。他们也被称为某些语言的匿名函数。虽然不同的语言对lambda有不同的语法,但原理是一样的,它们的各个部分通常是相同的。

也许最有名的是Javascript的匿名函数。

lol = function() {laugh()} 
# is equivalent to 
function lol() {laugh()} 

有什么区别?那么,有时你不想经历创建一个函数的麻烦,只是将它传递到某个地方,然后再也不会。

window.onload = function() {laugh()} 
# is way easier than 
function lol() {laugh()} 
window.onload = lol 

你可以看到the wikipedia article为indept信息,也可以直接在同一篇文章中跳到Lambda in programming

5

那么,你可以看到一个lambda作为一种快速的方法来编写一个你只想使用一次的方法。例如,以下方法

private int sum5(int n) 
{ 
    return n + 5; 
} 

相当于lambda:(n) => n + 5。除了你可以在你的类的任何地方调用方法,并且lambda生存在它声明的范围内(你也可以将lambda存储在Action和Func对象中)

lambda可以为你做的其他事情是捕获范围,建立关闭。举例来说,如果你有这样的事情:

...methodbody 
int acc = 5; 
Func<int> addAcc = (n) => n + acc; 

你有什么是可以接受的参数作为前一个功能,但添加量从变量的值取。即使在acc定义的范围完成之后,lambda仍可以存活。

你可以用lambda来构建非常整洁的东西,但是你必须小心,因为有时候你会因为这样的技巧而失去可读性。

6

因此,从可怕的定义开始--Lambda是另一种定义匿名方法的方法。有(从C#2.0我相信)是一种方法来构建匿名方法 - 但是,这种语法非常...不方便。

那么什么是匿名方法?这是定义内联方法的一种方法,没有名称 - 因此是匿名的。如果您有一个接受委托的方法,那么这很有用,因为您可以将此lambda表达式/匿名方法作为参数传递给予,因为类型匹配。以IEnumerable.Select为例,它的定义如下:

IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector); 

如果你使用这种方法通常在说一个列表,并选择两次,每次元素(即连结到本身):

string MyConcat(string str){ 
    return str + str; 
} 

... 

void myMethod(){ 
    IEnumerable<string> result = someIEnumerable.Select(MyConcat); 
} 

这是一个非常不方便的方法,特别是如果你想执行许多这些操作 - 通常这些类型的方法只能使用一次。你显示的定义(参数=>表达式)非常简洁,并且可以点击它。关于lambda的一个有趣的事情是,只要可以从表达式中得到它们,就不需要表达参数的类型 - 。 IE浏览器。在Select的情况下 - 我们知道第一个参数必须是TSource类型 - 因为方法的定义如此。更进一步,如果我们像foo.Select(...)那样调用方法,那么表达式的返回值将定义TResult。

一个有趣的事情是,对于单语句lambda的return关键字是不需要的 - lambda将返回任何一个表达式的计算结果。但是,如果您使用块(包裹在“{”和“}”中),那么您必须像往常一样包含return关键字。

如果有人愿意,定义参数的类型仍然是100%合法的。有了这个新的知识,让我们尝试并改写前面的例子:

void myMethod(){ 
    IEnumerable<string> result = someIEnumerable.Select(s => s + s); 
} 

或者有明确参数规定

void myMethod(){ 
    IEnumerable<string> result = someIEnumerable.Select((string s) => s + s); 
} 

的拉姆达在C#中使用它们来构建expression trees另一个有趣的功能。这可能不是“初学者”的材料 - 但简而言之,表达式树包含关于lambda的所有元数据,而不是可执行代码。

+0

@ kastermester:谢谢你的解释。我认为有一件事让我失望,那就是推断出的“参数类型”。做得好!! – IAbstract 2010-01-30 16:40:16

+0

你非常欢迎 - 很高兴我能帮上忙。在C#代码中遇到它们的前几次很难掌握这些 - 一旦你明白了,它们可以使更多的东西变得更容易 - 而且它们非常有用,而不仅仅是与LINQ。 – kastermester 2010-01-31 19:51:04

0

这只是C#写下函数值的符号。它不要求给函数一个名字,因此这个值有时被称为匿名函数。其他语言有其他符号,但它们总是包含参数列表和主体。

原始符号由Alonzo ChurchLambda calculus在1930ies用希腊字符拉姆达在表达式λx.t来表示一个功能,因此得名发明的。

2

正如其他人所说的,lambda表达式是函数的符号。它将绑定表达式右侧的自由变量与左侧的参数。

a => a + 1 

创建结合在表达式(a + 1)到一个函数的所述第一参数的自由变量,并返回该功能的功能。

Lambda是非常有用的一种情况是当你使用它们来处理列表结构。 System.Linq。可枚举类提供了许多有用的函数,使您可以使用实现IEnumerable的Lambda表达式和对象。例如Enumerable.Where可用于过滤列表:

List<string> fruits = new List<string> { 
     "apple", "passionfruit", "banana", "mango", 
     "orange", "blueberry", "grape", "strawberry" }; 

IEnumerable<string> shortFruits = fruits.Where(fruit => fruit.Length < 6); 

foreach (string fruit in shortFruits) { 
    Console.WriteLine(fruit); 
} 

输出将是“苹果,芒果,葡萄”。

试图了解是怎么回事:表达水果=> fruit.Length < 6创建其返回如果该参数的长度属性是一个函数小于6

Enumerable.Where循环通过列出并创建一个新的List,其中只包含提供的函数返回true的那些元素。这样可以节省您编写迭代List的代码,检查每个元素的谓词并执行某些操作。

0

我对理解lambda的基本知识的建议有两点。

首先,我推荐学习函数式编程。在这方面,Haskell是一个很好的语言。我正在使用的这本书,从Graham Hutton那里得到很多,其中有Programming in Haskell。这为Haskell提供了良好的基础,并包含lambda表达式的解释。

从那里我想你应该查看Erik Meijer's lectures on Functional Programming,因为他们给函数式编程提供了一个很好的介绍,也使用Haskell,并且跨越到C#中。

一旦你已经采取了一切,你应该很好地了解lambdas。

2

一个面向开发人员谁是firmiliar与编码,但不与lambda表达式好简单的解释是TekPub这个简单的视频

TekPub - Concepts: #2 Lambdas

你明明有很多在这里的反馈,但这是另一个很好的来源,一个简单的解释。

+0

+1不错的视频剪辑...我的声音坏了:(虽然我得到了它的要点... thx – IAbstract 2010-01-30 20:27:57

1

我知道这是有点老,但我来到这里,试图让这一切的拉姆达东西的感觉。 通过我浇筑完毕了所有的答案和评论的时候,我有拉姆达更好understnading,我想我应该只是添加这个简单的答案(从学习者的角度来学习):

不要混淆a => a + 1的含义加1将结果返回给a。 (这很可能是初学者混淆的原因 反而看起来像这样:a是函数的输入参数(未命名函数),+ 1是函数中的语句(未命名函数构造)飞')。

希望这有助于:)