匿名函式在計算機程式設計中是指一類無需定義識別符號（函式名）的函式或子程式，普遍存在於多種程式語言中。 1958 年 LISP 首先採用匿名函式，自此之後，越來越多程式語言陸續採用，主流的程式語言如 PHP 和 C 也陸續採用。 用途 排序 嘗試將類按名稱排序： a 10 10 100 asort lambda xy cmp x__class____name__ y__class____name__ print a 100 10 10 上述 100 的類名是 float，10 的類名是 int 而 10 的類名是 str，排列後的順序為 floatint，接著是 str。 該示例中的匿名函式就是 lambda 表示式： lambda xy cmp 該匿名函式接受兩個變數 x 和 y ，透過內部函式 cmp 返回兩者的比較值，下面的例子將按長度為字串列表排序： a three two four asort lambda xy cmp len x len y print a two four three 語言列表 語言 備註 ActionScript ALGOL 68 C 在有 clang 和 llvm 的 compilerrt 程式庫的環境下支援 C 從 C 30 （Net Farmework 35）開始支援 C 從 C11 開始支援，標準中稱之為 lambda 表示式（lambda expression） Clojure Curl D Delphi 從 Delphi 2009 開始支援 Dylan Erlang F Frink Go Haskell Java 從 Java 8 開始支援 JavaScript Kotlin Lisp Logtalk Lua Mathematica Matlab ML 語言 Objective Caml Standard ML etc Octave Object Pascal 原生支援匿名函式，其正式名稱為 匿名方法（anonymous method）。Oxygene Object Pascal 也支援匿名函式。 ObjectiveC Mac OS X 106 稱作 塊（block） Pascal Perl PHP 從 PHP 530 開始支援真匿名函式，之前則只支援部分匿名函式 Python Python 用 lambda 語法定義匿名函式，只需用表示式而無需宣告 R Ruby Ruby 的匿名函式源自 Smalltalk，也同樣叫 塊（block）。 Rust Rust 的匿名函式可以使用 閉包（Closures）來實現。 Scala Scheme Smalltalk Smalltalk 的匿名函式稱為 塊（block） Tcl Visual Basic NET v9 Visual Prolog v 72 Vala 示例 Python Python 用 lambda 語法定義匿名函式，只需用表示式而無需宣告 以下兩種相等同 1 不使用匿名函式 def f x return x x 2 使用匿名函式 lambda x x x JavaScript JavaScript 支援匿名函式。 alert function xreturn xx10 提示 100 小書籤也經常使用這種結構，例如下面的一個小書籤就將當前網頁的標題顯示為其 URL： javascriptdocumenttitlelocationhref 然而，由於該賦值語句返回了一個值（即 URL 本身），很多瀏覽器會同時建立一個新的頁面顯示這個值。 取而代之，下面的匿名函式就可以做到不返回任何值： javascriptfunction documenttitlelocationhref 第一對圓括號中的函式（function documenttitlelocationhref）用作宣告一個匿名函式，而最後的一對圓括號則用來執行這個函式。同等用法有： javascriptvar f function documenttitlelocationhref f PHP PHP 401 之前不支援匿名函式 httpphpnetcreate_function the top of the page indicates this with PHP 4 401 PHP 5。 401 至 53 PHP 401 新增加了 create_function 函式，這是匿名函式的雛形。該函式能建立一個隨機命名的新函式並以字串形式返回新函式的函式名。 foo create_function x return xx bar create_function x return xx echo foo 10 要注意的是，新函式本身及其變數都要放在單引號裡面，如果要放在雙引號之內，美元符號 則需要轉碼成為 。 53 PHP 53 新增加了 Closure 類，以及能使類的例項可被呼叫的 魔術方法__invoke 。Lambda 函式都是編譯器的一種 花招，它能產生新的能被呼叫的 Closure 例項，就像函式能被呼叫一樣。 x 3 func function z return z 2 echo func x 輸出結果為 6 上述例子中的 func 是 Closure 類的一個例項，而 echo func 則相當於是 func__invoke z。PHP 53 模仿使用匿名函式，但並非支援真匿名函式，因為 PHP 的函式仍非第一類函式。 雖然 PHP 53 支援閉包，但還需要像這樣明確標識其變數： x 3 func function use x x 2 func echo x 輸出結果為 6 func 引用了變數 x（x），在呼叫的時候就會修改原來的 x，其結果在函式以外的地方也是可見的。 C C 9803 C 9803 標準並不原生支援匿名函式。不過可以利用 Boost 庫的 BoostLambda 來實現一個匿名函式。 C 11 C11 標準提供了匿名函式的支援，在《ISOIEC 148822011》（C11 標準文件）中叫做 lambda 表示式該標準文件可以在 ISOIEC 148822011 Information technology Programming languages C 購買，或者可參看 2012 年一月份的標準草案檔案 N3337 ，第 512 節的 Lambda expressions。一個 lambda 表示式有如下的形式： capture parameters mutable exception attribute return_type body 必須用方括號括起來的 capture 列表來開始一個 lambda 表示式的定義。 lambda 函式的形參表比普通函式的形參表多了 3 條限制： 引數不能有預設值 不能有可變長引數列表 不能有無名引數 如果 lambda 函式沒有形參且沒有 mutable、exception 或 attribute 宣告，那麼引數的空圓括號可以省略。但如果需要給出 mutable、exception 或 attribute 宣告，那麼引數即使為空，圓括號也不能省略。 如果函式體只有一個 return 語句，或者返回值型別為 void，那麼返回值型別宣告可以被省略： captureparametersbody 一個 lambda 函式的例子如下： int x int y return x y 從 return 語句中隱式獲得的返回值型別 int x x 沒有 return 語句 lambda 函式的返回值為 void global_x 沒有引數，僅僅是訪問一個全域性變數 global_x 與前者相同， 可以被省略 在上面的第一個例子中這個無名函式的返回值是 decltype xy。如果 lambda 函式體的形式是 return expression，或者甚麼也沒返回，或者所有返回語句用 decltype 都能檢測到同一型別，那麼返回值型別可以被省略。 返回值型別可以顯式指定，如下所示： int x int y int int z x y return z 在這個例子中，一個臨時變數，z，被建立來儲存中間過程。與一般的函式一樣，中間值在呼叫的前後並不存在。甚麼也沒有返回的 lambda 表示式無需顯式指定返回值，沒有必要寫 void 程式碼。 lambda 函式可以捕獲 lambda 函式外的具有 automatic storage duration 的變數，即函式的區域性變數與函式形參變數。因而，具有 static storage duration 的變數不能被 lambda 捕獲，只能直接使用，這包括靜態區域性變數。函式體與這些變數的集合合起來稱做閉包。這些外部變數在宣告 lambda 表示式時列在在方括號 和 中。空的方括號表示沒有外界變數被 capture 或者按照預設方式捕獲外界變數。這些變數被傳值捕獲或者引用捕獲。對於傳值捕獲的變數，預設為只讀（這是由於 lambda 表示式生成的為一個函式物件，它的 operator 成員預設有 const 屬性）。修改這些傳值捕獲變數將導致編譯報錯。但在 lambda 表示式的引數表的圓括號後面使用 mutable 關鍵字，就允許 lambda 函式體內的語句修改傳值捕獲變數，這些修改與 lambda 表示式（實際上是用函式物件實現）有相同的生命期，但不影響被傳值捕獲的外部變數的值。lambda 函式可以直接使用具有 static 儲存期的變數。如果在 lambda 函式的捕獲列表中給出了 static 儲存期的變數，編譯時會給出警告，仍然按照 lambda 函式直接使用這些外部變數來處理。因此具有 static 儲存期的變數即使被宣告為傳值捕獲，修改該變數實際上直接修改了這些外部變數。編譯器生成 lambda 函式對應的函式物件時，不會用函式物件的資料成員來保持被 捕獲 的 static 儲存期的變數。示例： 沒有定義任何變數，但必須列出空的方括號。在 Lambda 表示式中嘗試使用任何外部變數都會導致編譯錯誤。 x y x 是按值傳遞，y 是按引用傳遞 任何被使用到的外部變數都按引用傳入。 任何被使用到的外部變數都按值傳入。 x x 按值傳入。其它變數按引用傳入。 z z 按引用傳入。其它變數按值傳入。 下面這個例子展示了 lambda 表示式的使用： stdvectorint some_list 1 2 3 4 5 int total 0 stdfor_each begin some_list end some_list totalint x total x 在類的非靜態成員函式中定義的 lambda 表示式可以顯式或隱式捕捉 this 指標，從而可以引用所在類物件的資料成員與函式成員。物件的 this 指標必需顯式捕獲宣告。因此，被捕獲的類的資料成員總是用 this 指標來訪問，如果 this 所指的物件不存在，則 this 是空懸指標。解決辦法是在定義 lambda 之前用區域性變數複製一份資料成員的值，然後捕獲這個區域性變數的值。 lambda 函式的函式體中，可以訪問下述變數： 函式引數 區域性宣告的變數 類資料成員：要求 lambda 表示式宣告在類成員函式中，物件的 this 指標必需顯式捕獲宣告。 具有靜態儲存期的變數（如全域性變數）。一般情況下，lambda 是用來捕獲區域性變數的，如果用其來捕獲全域性變數或者靜態變數，那麼編譯器會報 warning 被捕獲的外部變數 顯式捕獲的變數 隱式捕獲的變數，使用預設捕獲模式（傳值或引用）來訪問。 lambda 函式的資料型別是函式物件，儲存時必須用 stdfunction 模板型別或 auto 關鍵字。 例如： include functional include vector include iostream double eval stdfunction double double f double x 20 return f x int main stdfunctiondouble double f0 double xreturn 1 auto f1 double xreturn x decltype f0 fa 3 f0f1double xreturn xx stdvectordecltype f0 fv f0f1 fvpush_back double xreturn xx for int i0ifvsize i stdcout fv i20 stdendl for int i0i3i stdcout fa i20 stdendl for auto f fv stdcout f 20 stdendl for auto f fa stdcout f 20 stdendl stdcout eval f0 stdendl stdcout eval f1 stdendl stdcout eval double xreturn xx stdendl return 0 一個 lambda 函式的捕捉表示式為空，則可以用普通函式指標儲存或呼叫。例如： auto a_lambda_func int x void func_ptrint a_lambda_func func_ptr 4 calls the lambda C14 C11 中捕獲機制的侷限 lambda 捕獲的是區域性變數或形參，不管是按值還是按引用捕獲，這些都是具名的左值物件。而右值物件是匿名物件，無法被捕獲。 按值捕獲時，左值是被複制到閉包中的。如果被捕獲的物件是個只移動型別的物件時，因其無法被複制，就會出錯。 如果被捕獲的物件如果是一個佔用記憶體較大的物件時，按值捕獲顯然效率很低。 C14 增加了廣義捕獲 Generalized capture，或稱 初始化捕獲。MSDN In C14 you can introduce and initialize new variables in the capture clause without the need to have those variables exist in the lambda functions enclosing scope The initialization can be expressed as any arbitrary expression the type of the new variable is deduced from the type produced by the expression One benefit of this feature is that in C14 you can capture moveonly variables such as stdunique_ptr from the surrounding scope and use them in a lambda 即在捕獲子句（capture clause）中增加並初始化新的變數，該變數不需要在 lambda 表示式所處的閉包域（enclosing scope）中存在；即使在閉包域中存在也會被新變數覆蓋 override。新變數型別由它的初始化表示式推導。也就是說可以建立新的變數並在捕獲子句中對其進行初始化。這種方式稱之為帶有初始化程式的捕獲或者廣義 lambda 捕獲。一個用途是可以從閉包域中捕獲只供移動的變數並使用它。形如 mVar1 localVar1 mVar2 stdmove localVar2 C14 還允許 lambda 函式的形參使用 auto 關鍵字作為其型別，這實質上是函式物件的 operator 成員作為模板函式；並且允許可變引數模板。 auto a_lambda_func data1101int x 廣義捕獲，實質上是在函式物件中增加了資料成員 data1 並初始化 auto ptr stdmake_uniqueint10 See below for stdmake_unique auto lambda1 ptr stdmove ptr return ptr 大致等效於： auto lambda2 ptr stdmake_uniqueint10 return ptr auto lambda3 auto x auto y return x y lambda 函式的形參型別為 auto struct unnamed_lambda 這相當於函式物件： templatetypename T typename U auto operator T x U y const return x y auto lambda4 auto params 可變引數的函式模板 return foo stdforwarddecltype paramsparams Visual BasicNET 匿名函式或 lambda 表示式即無名的函式或過程，作為表示式的值。可以寫為一行或多行。例如： Dim func1Function i As integer i10 Dim action sub End Sub Dim func2 Function End Function 可以在宣告匿名函式的同時呼叫它。單行的 lambda 表示式不能使用 Return 關鍵字，其返回型別是自動推導得出；其引數要麼都是用 As 關鍵字指明型別，要麼全部是自動推匯出型別。 lambda 表示式在定義時可以使用所在上下文（context，即 C 語言的閉包 closure）的區域性變數、引數、屬性、Me 等等的值，即使 lambda 表示式離開了定義時所在的 context，這些被使用的區域性變數等的值仍然有效。這是因為 lambda 表示式在定義時把所用到的 context 的值儲存到它自己的定義類中。lambda 表示式可以巢狀定義。 參考資料 外部連結 《深入理解 PHP 核心》第四章 函式的實現 第四節 匿名函式及閉包 Category 資料型別 CategoryLambda 演算 Category 子程式 lambda 表示式 Category 帶有程式碼示例的條目