2010-01-30 69 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是函數中的語句(未命名函數構造)飛')。

希望這有助於:)