2010-09-15 161 views
3

我還沒有真正能夠牢牢把握創建和使用lambda表達式。我知道如何在linq語句中使用它們,但我真的不明白幕後發生了什麼。我也還沒有能夠找到關於何時使用它們完整的教程,如何去界定等我似乎無法讓我的頭在c#中的Lambdas附近#

第二部分...

他們說,JavaScript是一種語言LAMBDA,我知道的JavaScript相當好,只是想知道什麼類型的概念適用於JavaScript lambda和c#lambda。

THRID部分...

就是一個功能性的語言和語言拉姆達之間的區別?

有什麼建議嗎?

+1

您可能被命名嚇倒了。如果你知道數學中有什麼函數(比如x-> x^2),那麼你就已經瞭解了lambda。 – greenoldman 2010-09-15 06:50:45

回答

8

讓我來告訴你後臺發生了什麼事。它比你想象的更直接。

假設您有:

delegate int D(int x); 
... 
class C 
{ 
    void M(int y) 
    { 
     int z = 123; 
     D d = x=>x+y+z; 
     Console.WriteLine(d(10)); 
     z = 345; 
     y = 789; 
     Console.WriteLine(d(10)); 
    } 
} 

所有的編譯器是假裝你寫道:

delegate int D(int x); 
... 
class C 
{ 
    private class Locals 
    { 
     public int y; 
     public int z; 
     public int A(int x) 
     { 
      return x + this.y + this.z; 
     } 
    } 
    void M(int y) 
    { 
     // Initialize the closure class: 

     Locals locals = new Locals(); 
     locals.y = y; 

     // Transform the body so that all usages of y, z and the lambda 
     // refer to the closure class: 

     locals.z = 123; 
     D d = locals.A; 
     Console.WriteLine(d(10)); // Calls locals.A(10) 
     locals.z = 345; 
     locals.y = 789; 
     Console.WriteLine(d(10)); // Calls locals.A(10) 

    } 
} 

這一切就是這麼簡單。 lambda只是一個緊湊的語法,用於編寫「將lambda使用的所有外部局部變量提升到一個類中,使用給定的主體對該類進行方法,並使我成爲該方法的代表」。

JScript中的函數閉包的工作方式基本相同。JScript當然不是一個基於類的語言,所以細節略有不同,但這個想法是一樣的。在C#中,新創建的委託對象跟蹤具有變量狀態的局部類。在JScript中,新創建的函數對象具有對創建閉包的函數的激活框架的引用,它基本上是相同的信息。

將lambda表達式轉換爲表達式樹的方式相當不同,但這至少應該讓您開始瞭解lambda表達式的概念。

+0

在'locals locals = new Locals();'和'locals.z = z;'上面的一行代替'locals.z = 123'代替'int z = 123;'會更準確嗎? ''你的演示代碼似乎有點偏離,因爲在'M'後面的lambda之外的地方使用了z。或者,也許我的建議讓事情變得更糟,因爲如果y或z在代表聲明和writeline之間被修改。 – Brian 2010-09-15 13:51:09

+3

@布萊恩:它不是*更精確*,它*不正確*。閉包捕獲*整個變量*,而不是閉包創建時的值*。沒有更多的局部變量z;它完全消失*。如果z在M中的lambda之外的某處使用,那麼z也會變成locals.z。 *由lambda轉換爲z的狀態在lambda *之外是可見的。再次,讓我重複一遍以確保它清晰:封閉捕獲**變量**,而不是**其值**。很多人對此感到困惑。 – 2010-09-15 14:21:24

+1

@Eric Lippert:那麼不是'locals.y = y;'不正確?是的,我意識到語法不允許你說'locals.y是y的別名;' – Brian 2010-09-15 14:43:34

5

我不能回答三部分,但讓我採取搖擺在1 & 2,也許,這將有助於你與3

你懂的代表?因爲這就是你正在處理的一切。 VS2010 documentation非常簡潔。

在根中,lambda只是傳遞給委託聲明的匿名函數。 或者,更簡單的方法沒有簽名(返回類型,名稱和參數)。該用法暗示簽名。

詢問「何時使用lambda」實際上提出了「何時應該爲委託使用匿名函數」的問題,實際上,我想不出比LINQ使用它們更好的場景作爲示例。 AFAIK和理解,JavaScript是一種lambda語言,因爲你可以像變量一樣傳遞方法,並且使用匿名方法來傳遞方法,而不僅僅是聲明的方法。

就閱讀拉姆達而言,我不喜歡某些人使用的「去」術語。我傾向於像一個正常的方法,這是說來閱讀:

「給出的參數列表部分,執行該代碼」

所以, 排=> row.State ==「NY」 手段 「給定行,當行的狀態爲紐約時返回true」。

也許我太簡單了,但我是一個簡單的傢伙。

+0

完全符合您閱讀lambda的方法。這個「去」的東西聽起來很愚蠢。 – Dave 2010-09-15 04:01:50

0

簡介中的C#3以及Jon Skeet的書在他們的書中很好地解決了這個問題。

0

lambda表達式是一個匿名的,一流的函數。當你聲明一個,你創建一個可尋址的函數,但可以有任意數量的別名,就像一個對象(因此,第一類)。你可以在變量和參數中傳遞它。 Javascript可以稱爲lambda語言,因爲所有的javascript函數都具有這些屬性。它們可以被實例化,別名和通過,並且不需要像C do中的函數那樣具有名稱。所以,他們是匿名的,一流的功能。

+0

所以主要區別,正如我所看到的那樣,javascript中的lambdas本身可以具有屬性,而在c#中則不會。那是對的嗎? – 2010-09-15 15:19:27

+0

這是正確的,但我不會說它是主要區別。這種差異主要是動態(Javascript)與靜態(C#)輸入的副作用。當你在C#中使用lambda時,它的類型完全由它的簽名來定義。 JavaScript中的匿名方法也是如此,除了在Javascript中,您可以在實例化後爲其生成的對象添加屬性。另外,在Javascript中,你會注意到沒有真正的簽名,並且沒有類型約束 - 它是鴨子類型的。 – 2010-09-15 17:11:58

0

我會在你的問題的第一部分。可能值得重新發布第二部分作爲單獨的問題。

我喜歡將lambdas作爲我定義的函數/過程,而不是模塊級別來定義。

所以,讓我們說我有這樣的代碼:

public void Run() 
{ 
    var template = "{0} inches is {1} centimetres."; 
    Console.WriteLine(template, 2.0, 2.0 * 2.54); 
    Console.WriteLine(template, 5.0, 5.0 * 2.54); 
} 

顯然,我不想重複表達* 2.54這樣我就可以這樣定義一個函數:

private double Inches2Centimetres(double inches) 
{ 
    return inches * 2.54; 
} 

而且我的代碼現在變成:

public void Run() 
{ 
    var template = "{0} inches is {1} centimetres."; 
    Console.WriteLine(template, 2.0, Inches2Centimetres(2.0)); 
    Console.WriteLine(template, 5.0, Inches2Centimetres(5.0)); 
} 

但是這個功能與調用代碼分開,如果這是我需要計算的唯一地方,我實際上正在傷害我的代碼的可維護性。

我可以決定使用lambda,這樣我就可以代替這樣寫:

public void Run() 
{ 
    Func<double, double> inches2Centimetres = inches => inches * 2.54; 

    var template = "{0} inches is {1} centimetres."; 
    Console.WriteLine(template, 2.0, inches2Centimetres(2.0)); 
    Console.WriteLine(template, 5.0, inches2Centimetres(5.0)); 
} 

至於我的調用代碼而言,Inches2Centimetres & inches2Centimetres之間的區別是函數名的外殼。但是對於代碼清潔和可維護性來說,lambda的使用給了我更好的封裝。

現在,由於函數被定義爲Func<...>類型的變量,所以您可以像變量一樣傳遞lambda表達式。

下面是一個很好的例子,它可以是有用的:我會經常創建類似數據庫連接的擴展方法來隱藏管道gumpf,從而留下我正在編寫的代碼的本質。

public static T[] UsingDataReader<T>(
    this IDbConnection @this, 
    string query, 
    Func<IDataReader, T> transform) 
{ 
    //... 
} 

在這種情況下lamdba,Func<IDataReader, T> transform然後可以當我調用該函數傳入,而且只需要知道如何把當前行到T類型的對象。所有用於打開連接,執行閱讀器命令,在記錄之間移動以及乾淨地關閉所有內容的代碼都由擴展方法處理。我的調用代碼要簡潔得多。

string[] names = conn.UsingDataReader<string>("select FirstName from People;", 
    dr => dr["FirstName"] as string); 

我知道我已經提供了一個非常簡單的答案給你的問題,但我希望它有幫助。

0

傳統Lambda演算中的函數是一個函數,它對單個值進行操作並返回一個單值 - 一元函數。它們也是非常簡單的功能,沒有做任何特別複雜的事情(通常將一組映射到另一組)。爲了做複雜的工作,你可以鏈接函數,這樣第一個函數的輸出就是第二個函數的輸入。

在C#中的Lambdas只是一樣的函數。我們使用它們來投影,轉換,過濾或排序數據。

將'Lambda'看作是一個函數,它接受參數並返回一個值。

4

我可以解決JavaScript部分。因爲你可以在JS(var fn = function(){/* stuff */};)中聲明匿名函數,所以你也可以將這些函數作爲參數傳遞給其他函數,事實上,如果你需要做一個自定義的排序例程,那麼你已經使用了lambda表達式,例如:

// Standard sort: 
x = [4,3,6,7,1,5,2]; 
x.sort(); 

// Custom sort: 
y = [ 
    {'val':4,'name':'four'}, 
    {'val':3,'name':'three'}, 
    {'val':6,'name':'six'}, 
    {'val':7,'name':'seven'}, 
    {'val':1,'name':'one'}, 
    {'val':5,'name':'five'}, 
    {'val':2,'name':'two'}, 
]; 
y.sort(function(a,b){ return a.val > b.val ? 1 : -1 }); 

replace()是另一個例子,採取了lambda函數作爲參數。

在自己的代碼這樣做是很容易的,但實際上我從來沒有發現一個情況,當這不可能做到更清楚一些其他方式(如果其他人需要管理你的代碼,你必須打破他們的頭,直到他們看到拉姆達)

下面是一個示例。假設你有一個產生某種形式的輸出的對象Widget。你知道它總是會產生輸出,但是你不知道輸出的結果是什麼。一種解決方案是將對象生成輸出所需的方法傳遞給對象。這裏有一個簡單的實現:

首先,Widget本身。需要注意的是Widget.prototype.publish()只有一個參數,這是你的自定義格式:

var Widget = function() { 
    var self = this; 
    var strPrivateVar = "This is a private variable"; 
    self.publicVar = "This is a default public variable"; 
    self.publish = function(f) { 
     var fnFormatter = f; 
     var strOutput = "The output is " + fnFormatter(self,strPrivateVar); 
     return strOutput; 
    } 
}; 

接下來,你的格式化。一個簡要總結,而另一個給人的全文:

var fnSummary = function(o,s) { 
    var self = o; 
    var strPrivateVar = s; 
    return strPrivateVar.substr(0,5) + ' ' + self.publicVar.substr(0,5); 
} 
var fnDetails = function(o,s) { 
    var self = o; 
    var strPrivateVar = s; 
    return strPrivateVar + ' ' + self.publicVar; 
} 

而在去年,您的實現:

var wWidget = new Widget(); 
wWidget.publicVar = "I have overridden the public property"; 
var strSummary = wWidget.publish(fnSummary); 
var strDetails = wWidget.publish(fnDetails); 
console.log(strSummary,strDetails); 

這種解決方案意味着你不需要改變wWidget對象,以獲得所需輸出。由於範圍問題,您必須跳過一些環節才能將對象中的變量轉換爲發佈方法,但一旦完成了這些操作,其餘的操作就很容易了。

我知道還有其他人可以舉一個更好的例子,但我希望這可以幫助你。

相關問題