共找到2條詞條名為Lambda表達式的結果 展開
- 匿名函數之一
- 匿名函數
Lambda表達式
匿名函數之一
“Lambda 表達式”(lambda expression)是一個匿名函數,Lambda表達式基於數學中的λ演算得名,直接對應於其中的lambda抽象(lambda abstraction),是一個匿名函數,即沒有函數名的函數。Lambda表達式可以表示閉包(注意和數學傳統意義上的不同)。
C#的Lambda 表達式都使用 Lambda 運算符 =>,該運算符讀為“goes to”。語法如下:
1 | (object argOne, object argTwo) => {; } |
函數體多於一條語句的可用大括弧括起。
類型
可以將此表達式分配給委託類型,如下所示:
1 2 3 | delegate int del(int i); del myDelegate=x=>{return x*x;}; int j = myDelegate(5); //j=25 |
創建表達式目錄樹類型:
1 2 3 | using System.Linq.Expressions; //... Expression |
=> 運算符具有與賦值運算符 (=) 相同的優先順序,並且是右結合運算符。
Lambda 用在基於方法的 LINQ 查詢中,作為諸如 Where 和 Where 等標準查詢運算符方法的參數。
使用基於方法的語法在 Enumerable 類中調用 Where 方法時(像在 LINQ to Objects 和 LINQ to XML 中那樣),參數是委託類型 System..::.Func<(Of <(T, TResult>)>)。使用 Lambda 表達式創建委託最為方便。例如,當您在 System.Linq..::.Queryable 類中調用相同的方法時(像在 LINQ to SQL 中那樣),則參數類型是 System.Linq.Expressions..::.Expression,其中 Func 是包含至多五個輸入參數的任何 Func 委託。同樣,Lambda 表達式只是一種用於構造表達式目錄樹的非常簡練的方式。儘管事實上通過 Lambda 創建的對象的類型是不同的,但 Lambda 使得 Where 調用看起來類似。
在前面的示例中,請注意委託簽名具有一個 int 類型的隱式類型輸入參數,並返回 int。可以將 Lambda 表達式轉換為該類型的委託,因為該表達式也具有一個輸入參數 (x),以及一個編譯器可隱式轉換為 int 類型的返回值。(以下幾節中將對類型推理進行詳細討論。)使用輸入參數 5 調用委託時,它將返回結果 25。
在 is 或 as 運算符的左側不允許使用 Lambda。
適用於匿名方法的所有限制也適用於 Lambda 表達式。有關更多信息,請參見匿名方法(C# 編程指南)。
特殊
下列規則適用於 Lambda 表達式中的變數範圍:
捕獲的變數將不會被作為垃圾回收,直至引用變數的委託超出範圍為止。
在外部方法中看不到 Lambda 表達式內引入的變數。
Lambda 表達式無法從封閉方法中直接捕獲 ref 或 out 參數。
Lambda 表達式中的返回語句不會導致封閉方法返回。
Lambda 表達式不能包含其目標位於所包含匿名函數主體外部或內部的 goto 語句、break 語句或 continue 語句。
Lambda表達式的本質是“匿名方法”,即當編譯我們的程序代碼時,“編譯器”會自動將“Lambda表達式”轉換為“匿名方法”,如下例:
1 2 3 | string[] names={"agen","balen","coure","apple"}; string[] findNameA=Array.FindAll string[] findNameB=Array.FindAll |
上面中兩個FindAll方法的反編譯代碼如下:
1 2 | string[] findNameA=Array.FindAll string[] findNameB=Array.FindAll |
從而可以知道“Lambda表達式”與“匿名方法”是可以劃上等號的,只不過使用“Lambda表達式”輸寫代碼看上去更直觀漂亮,不是嗎?
Lambda表達式的語法格式:
參數列表 => 語句或語句塊
其中“參數列”中可包含任意個參數(與委託對應),如果參數列中有0個或1個以上參數,則必須使用括弧括住參數列,如下:
1 2 3 4 5 6 7 8 9 | x => x + 1 // Implicitly typed, expression body x => { return x + 1; } // Implicitly typed, statement body (int x) => x + 1 // Explicitly typed, expression body (int x) => { return x + 1; } // Explicitly typed, statement body (x, y) => x * y // Multiple parameters () => Console.WriteLine() // No parameters async (t1,t2) => await t1 + await t2 // Async delegate (int x) { return x + 1; } // Anonymous method expression delegate { return 1 + 1; } // Parameter list omitted |
如果“語句或語句塊”有返回值時,如果只有一條語句則可以不輸寫“return”語句,編譯器會自動處理,否則必須加上,如下示例:
“Lambda表達式”是委託的實現方法,所以必須遵循以下規則:
1)“Lambda表達式”的參數數量必須和“委託”的參數數量相同;
2)如果“委託”的參數中包括有ref或out修飾符,則“Lambda表達式”的參數列中也必須包括有修飾符;
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class Test { delegate int AddHandler(int x,int y); static void Print(AddHandler add); { Console.Write(add(1, 3)); } static void Main(string[] args) { Print((x,y) => x+y); Print((x,y) => {int v=x*10; return y+v;}); Console.ReadKey(); } } |
註:如果包括有修飾符,則“Lambda表達式”中的參數列中也必須加上參數的類型
3)如果“委託”有返回類型,則“Lambda表達式”的“語句或語句塊”中也必須返回相同類型的數據;
4)如果“委託”有幾種數據類型格式而在“Lambda表達式”中“編譯器”無法推斷具體數據類型時,則必須手動明確數據類型。
例子:
(錯誤代碼)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class Test { delegate AddHandler static void Print(AddHandler { Console.WriteLine("int type:{0}",test(1, 2)); } static void Print(AddHandler { Console.WriteLine("doubletype:{0}",test(1d, 2d)); } static void Main(string[] args) { Print((x, y) => x+y); Console.ReadKey(); } } |
當我們編譯以下代碼時,編譯器將會顯示以下錯誤信息:
1 2 3 | 在以下方法或屬性之間的調用不明確: "ConsoleApplication1.Test.Print(ConsoleApplication1.Test.AddHandler 和"ConsoleApplication1.Test.Print(ConsoleApplication1.Test.AddHandler |
所以我們必須明確數據類型給編譯器,如下:
1 | Print((int x, int y) => x+y); |
這樣我們的代碼就能編譯通過了。
Java表達式
Java 8的一個大亮點是引入Lambda表達式,使用它設計的代碼會更加簡潔。當開發者在編寫Lambda表達式時,也會隨之被編譯成一個函數式介面。下面這個例子就是使用Lambda語法來代替匿名的內部類,代碼不僅簡潔,而且還可讀。
沒有使用Lambda的老方法:
1 2 3 4 5 | button.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent actionEvent){ System.out.println("Action detected"); } }); |
使用Lambda:
1 2 3 | button.addActionListener( actionEvent -> { System.out.println("Action detected"); }); |
讓我們來看一個更明顯的例子。
不採用Lambda的老方法:
1 2 3 4 5 6 | Runnable runnable1=new Runnable(){ @Override public void run(){ System.out.println("Running without Lambda"); } }; |
使用Lambda:
1 | Runnable runnable2=()->System.out.println("Running from Lambda"); |
正如你所看到的,使用Lambda表達式不僅讓代碼變的簡單、而且可讀、最重要的是代碼量也隨之減少很多。然而,在某種程度上,這些功能在Scala等這些JVM語言里已經被廣泛使用。
並不奇怪,Scala社區是難以置信的,因為許多Java 8里的內容看起來就像是從Scala里搬過來的。在某種程度上,Java 8的語法要比Scala的更詳細但不是很清晰,但這並不能說明什麼,如果可以,它可能會像Scala那樣構建Lambda表達式。
一方面,如果Java繼續圍繞Lambda來發展和實現Scala都已經實現的功能,那麼可能就不需要Scala了。另一方面,如果它只提供一些核心的功能,例如幫助匿名內部類,那麼Scala和其他語言將會繼續茁壯成長,並且有可能會凌駕於Java之上。其實這才是最好的結果,有競爭才有進步,其它語言繼續發展和成長,並且無需擔心是否會過時。
C++表達式
ISO C++ 11 標準的一大亮點是引入Lambda表達式。基本語法如下:
1 | [capture list] (parameter list) -> return type { function body } |
其中除了“[ ]”(其中捕獲列表可以為空)和“複合語句”(相當於具名函數定義的函數體),其它都是可選的。它的類型是單一的具有成員operator()的非聯合的類類型,稱為閉包類型(closure type)。
C++中,一個lambda表達式表示一個可調用的代碼單元。我們可以將其理解為一個未命名的內聯函數。它與普通函數不同的是,lambda必須使用位置返回來指定返回類型。
例如調用中的std::sort,ISO C++ 98 的寫法是要先寫一個compare函數:
1 2 3 4 | bool compare(int& a,int& b) { return a>b; } |
然後,再這樣調用:
1 | sort(a, a+n, compare); |
然而,用ISO C++ 11 標準新增的Lambda表達式,可以這麼寫:
1 | sort(a, a+n, [](int a,int b){return a>b;});//降序排序 |
這樣一來,代碼明顯簡潔多了。
由於Lambda的類型是單一的,不能通過類型名來顯示聲明對應的對象,但可以利用auto關鍵字和類型推導:
1 | auto f=[](int a,int b){return a>b;}; |
和其它語言的一個較明顯的區別是Lambda和C++的類型系統結合使用,如:
1 2 3 4 | auto f=[x](int a,int b){return a>x;};//x被捕獲複製 int x=0, y=1; auto g=[&](int x){return ++y;};//y被捕獲引用,調用g後會修改y,需要注意y的生存期 bool(*fp)(int, int)=[](int a,int b){return a>b;};//不捕獲時才可轉換為函數指針 |
Lambda表達式可以嵌套使用。
ISO C++14支持基於類型推斷的泛型lambda表達式。上面的排序代碼可以這樣寫:
1 | sort(a, a+n, [](const auto& a,const auto& b){return a>b;});//降序排序:不依賴a和b的具體類型 |
因為參數類型和函數模板參數一樣可以被推導而無需和具體參數類型耦合,有利於重構代碼;和使用auto聲明變數的作用類似,它也允許避免書寫過於複雜的參數類型。特別地,不需要顯式指出參數類型使使用高階函數變得更加容易。
Lambda表達式是Python中一類特殊的定義函數的形式,使用它可以定義一個匿名函數。與其它語言不同,Python的Lambda表達式的函數體只能有單獨的一條語句,也就是返回值表達式語句。其語法如下:
lambda 形參列表 : 函數返回值表達式語句
下面是個Lambda表達式的例子:
1 2 3 4 | #!/usr/bin/envpython li=[{"age":20,"name":"def"},{"age":25,"name":"abc"},{"age":10,"name":"ghi"}] li=sorted(li, key=lambda x:x["age"]) print(li) |
如果不用Lambda表達式,而要寫成常規的函數,那麼需要這麼寫:
1 2 3 4 5 6 | #!/usr/bin/envpython def comp(x): return x["age"] li=[{"age":20,"name":"def"},{"age":25,"name":"abc"},{"age":10,"name":"ghi"}] li=sorted(li, key=comp) print(li) |