匿名函式

匿名函式的函式或子程式，普遍存在於多種程式語言中。1958年LISP首先採用匿名函式，自此之後，越來越多程式語言陸續採用，主流的程式語言如PHP和C++也陸續採用。用途.排序.嘗試將類按名稱排序：a = [10, '10', 10.0]a.sort(lambda x,y: cmp(x.__class__.__name__, y.__class__.__name__))print a[10.0, 10, '10']上述 codice_1 的類名是“codice_2”，codice_3 的類名是“codice_4”而 codice_5 的類名是“codice_6”，排列後的順序為“codice_2”“codice_4”，接著是“codice_6”。該示例中的匿名函式就是lambda表示式：lambda x,y: cmp(...)該匿名函式接受兩個變數 codice_10 和 codice_11 ，透過內部函式 codice_12 返回兩者的比較值，下面的例子將按長度為字串列表排序：a = ['three', 'two', 'four']a.sort(lambda x,y: cmp(len(x), len(y)))print a['two', 'four', 'three']示例.Python.Python用lambda語法定義匿名函式，只需用表示式而無需宣告def f(x):return x * xlambda x: x * xJavaScript.JavaScript支援匿名函式。alert((function(x){return x*x;})(10)); // 提示100小書籤也經常使用這種結構，例如下面的一個小書籤就將當前網頁的標題顯示為其URL：javascript:document.title=location.href;然而，由於該賦值語句返回了一個值，很多瀏覽器會同時建立一個新的頁面顯示這個值。取而代之，下面的匿名函式就可以做到不返回任何值：javascript:(function(){document.title=location.href;})();第一對圓括號中的函式用作宣告一個匿名函式，而最後的一對圓括號則用來執行這個函式。同等用法有：javascript:var f = function(){document.title=location.href;}; f();PHP.PHP 4.0.1之前不支援匿名函式。4.0.1 至 5.3.PHP 4.0.1新增加了codice_13函式，這是匿名函式的雛形。該函式能建立一個隨機命名的新函式並以字串形式返回新函式的函式名。$foo = create_function('$x', 'return $x*$x;');$bar = create_function("\$x", "return \$x*\$x;");echo $foo(10);要注意的是，新函式本身及其變數都要放在單引號裡面，如果要放在雙引號之內，美元符號“$”則需要轉碼成為“\$”。5.3.PHP 5.3新增加了codice_14類，以及能使類的例項可被呼叫的“魔術方法”codice_15。Lambda函式都是編譯器的一種“花招”，它能產生新的能被呼叫的codice_14例項，就像函式能被呼叫一樣。$x = 3;$func = function($z) { return $z *= 2; };echo $func($x); // 輸出結果為6上述例子中的codice_17是codice_14類的一個例項，而codice_19則相當於是codice_20。PHP 5.3模仿使用匿名函式，但並非支援真匿名函式，因為PHP的函式仍非第一類函式。雖然PHP 5.3支援閉包，但還需要像這樣明確標識其變數：$x = 3;$func = function() use(&amp;$x) { $x *= 2; };$func();echo $x; // 輸出結果為6codice_17引用了變數codice_22，在呼叫的時候就會修改原來的$x，其結果在函式以外的地方也是可見的。C++.C++ 98/03.C++ 98/03標準並不原生支援匿名函式。不過可以利用Boost庫的Boost.Lambda來實現一個匿名函式。C++ 11.C++11標準提供了匿名函式的支援，在《ISO/IEC 14882:2011》中叫做lambda表示式。一個lambda表示式有如下的形式：[capture] (parameters) mutable exception attribute -&gt; return_type { body }必須用方括號括起來的capture列表來開始一個lambda表示式的定義。lambda函式的形參表比普通函式的形參表多了3條限制：如果lambda函式沒有形參且沒有mutable、exception或attribute宣告，那麼引數的空圓括號可以省略。但如果需要給出mutable、exception或attribute宣告，那麼引數即使為空，圓括號也不能省略。如果函式體只有一個return語句，或者返回值型別為void，那麼返回值型別宣告可以被省略：一個lambda函式的例子如下：[](int x, int y) { return x + y; } // 從return語句中隱式獲得的返回值型別[](int&amp; x) { ++x; } // 沒有return語句 -&gt; lambda函式的返回值為void[]() { ++global_x; } // 沒有引數，僅僅是訪問一個全域性變數[]{ ++global_x; } // 與前者相同，()可以被省略在上面的第一個例子中這個無名函式的返回值是codice_23。如果lambda函式體的形式是codice_24，或者甚麼也沒返回，或者所有返回語句用codice_25都能檢測到同一型別，那麼返回值型別可以被省略。返回值型別可以顯式指定，如下所示：在這個例子中，一個臨時變數，codice_26，被建立來儲存中間過程。與一般的函式一樣，中間值在呼叫的前後並不存在。甚麼也沒有返回的lambda表示式無需顯式指定返回值，沒有必要寫codice_27程式碼。lambda函式可以捕獲lambda函式外的具有automatic storage duration的變數，即函式的區域性變數與函式形參變數。因而，具有static storage duration的變數不能被lambda捕獲，只能直接使用，這包括靜態區域性變數。函式體與這些變數的集合合起來稱做閉包。這些外部變數在宣告lambda表示式時列在在方括號codice_28和codice_29中。空的方括號表示沒有外界變數被capture或者按照預設方式捕獲外界變數。這些變數被傳值捕獲或者引用捕獲。對於傳值捕獲的變數，預設為只讀有相同的生命期，但不影響被傳值捕獲的外部變數的值。lambda函式可以直接使用具有static儲存期的變數。如果在lambda函式的捕獲列表中給出了static儲存期的變數，編譯時會給出警告，仍然按照lambda函式直接使用這些外部變數來處理。因此具有static儲存期的變數即使被宣告為傳值捕獲，修改該變數實際上直接修改了這些外部變數。編譯器生成lambda函式對應的函式物件時，不會用函式物件的資料成員來保持被“捕獲”的static儲存期的變數。示例：[] // 沒有定義任何變數，但必須列出空的方括號。在Lambda表示式中嘗試使用任何外部變數都會導致編譯錯誤。[x, &amp;y] // x是按值傳遞，y是按引用傳遞[&amp;] // 任何被使用到的外部變數都按引用傳入。[=] // 任何被使用到的外部變數都按值傳入。[&amp;, x] // x按值傳入。其它變數按引用傳入。[=, &amp;z] // z按引用傳入。其它變數按值傳入。下面這個例子展示了lambda表示式的使用：std::vector some_list{ 1, 2, 3, 4, 5 };int total = 0;std::for_each(begin(some_list), end(some_list),在類的非靜態成員函式中定義的lambda表示式可以顯式或隱式捕捉codice_31指標，從而可以引用所在類物件的資料成員與函式成員。物件的this指標必需顯式捕獲宣告。因此，被捕獲的類的資料成員總是用this指標來訪問，如果this所指的物件不存在，則this是空懸指標。解決辦法是在定義lambda之前用區域性變數複製一份資料成員的值，然後捕獲這個區域性變數的值。lambda函式的函式體中，可以訪問下述變數：lambda函式的資料型別是函式物件，儲存時必須用codice_32模板型別或codice_33關鍵字。例如：double eval(std::function  f, double x = 2.0)return f(x);int main()std::function f0 = [](double x){return 1;};auto f1 = [](double x){return x;};decltype(f0) fa[3] = {f0,f1,[](double x){return x*x;}};std::vector fv = {f0,f1};fv.push_back ([](double x){return x*x;});for(int i=0;i&lt;fv.size();i++)std::cout « fv[i](2.0) « std::endl;for(int i=0;i&lt;3;i++)std::cout « fa[i](2.0) « std::endl;for(auto &amp;f : fv)std::cout « f(2.0) « std::endl;for(auto &amp;f : fa)std::cout « f(2.0) « std::endl;std::cout « eval(f0) « std::endl;std::cout « eval(f1) « std::endl;std::cout « eval([](double x){return x*x;}) « std::endl;return 0;一個lambda函式的捕捉表示式為空，則可以用普通函式指標儲存或呼叫。例如：auto a_lambda_func = [](int x) { /*...*/ };void (* func_ptr)(int) = a_lambda_func;func_ptr(4); //calls the lambda.C++14.C++11中捕獲機制的侷限C++14增加了廣義捕獲(Generalized capture)，或稱“初始化捕獲”。即在捕獲子句中存在；即使在閉包域中存在也會被新變數覆蓋(override)。新變數型別由它的初始化表示式推導。也就是說可以建立新的變數並在捕獲子句中對其進行初始化。這種方式稱之為帶有初始化程式的捕獲或者廣義lambda捕獲。一個用途是可以從閉包域中捕獲只供移動的變數並使用它。形如[mVar1 = localVar1, mVar2 = std::move(localVar2)](){};C++14還允許lambda函式的形參使用codice_33關鍵字作為其型別，這實質上是函式物件的codice_30成員作為模板函式；並且允許可變引數模板。auto a_lambda_func = [data1=101](int x) { /*...*/ }; //廣義捕獲，實質上是在函式物件中增加了資料成員data1並初始化auto ptr = std::make_unique(10); //See below for std::make_unique//大致等效於：auto lambda3 = [](auto x, auto y) {return x + y;} //lambda函式的形參型別為autostruct unnamed_lambda //這相當於函式物件：templateauto lambda4 = [](auto&amp;&amp;... params) //可變引數的函式模板return (foo(std::forward(params)...));Visual Basic.NET.匿名函式或lambda表示式即無名的函式或過程，作為表示式的值。可以寫為一行或多行。例如：Dim func1=Function(i As integer) i+10Dim action = sub()End SubDim func2 = Function()End Function可以在宣告匿名函式的同時呼叫它。單行的lambda表示式不能使用Return關鍵字，其返回型別是自動推導得出；其引數要麼都是用As關鍵字指明型別，要麼全部是自動推匯出型別。lambda表示式在定義時可以使用所在上下文的區域性變數、引數、屬性、Me等等的值，即使lambda表示式離開了定義時所在的context，這些被使用的區域性變數等的值仍然有效。這是因為lambda表示式在定義時把所用到的context的值儲存到它自己的定義類中。lambda表示式可以巢狀定義。