匿名函数

匿名函数（Anonymous Function）在计算机编程中是指一类无需定义标识符（函数名）的函数或子程序，普遍存在于多种编程语言中。
1958年LISP首先采用匿名函数，自此之后，越来越多编程语言陆续采用，主流的编程语言如PHP和C++也陆续采用。
用途.
排序.
尝试将类按名称排序：
<syntaxhighlight lang="python">
a = '10', 10.0
a.sort(lambda x,y: cmp(x.__class__.__name__, y.__class__.__name__))
print a
10, '10'
</syntaxhighlight>
上述 codice_1 的类名是“codice_2”，codice_3 的类名是“codice_4”而 codice_5 的类名是“codice_6”，排列后的顺序为“codice_2”“codice_4”，接着是“codice_6”。
该示例中的匿名函数就是lambda表达式：
<syntaxhighlight lang="python">
lambda x,y: cmp(...)
</syntaxhighlight>
该匿名函数接受两个变量 codice_10 和 codice_11 ，通过内部函数 codice_12 返回两者的比较值，下面的例子将按长度为字符串列表排序：
<syntaxhighlight lang="python">
a = ['three', 'two', 'four']
a.sort(lambda x,y: cmp(len(x), len(y)))
print a
['two', 'four', 'three']
</syntaxhighlight>
示例.
Python.
Python用lambda语法定义匿名函数，只需用表达式而无需声明
<syntaxhighlight lang="python">
def f(x):
 return x * x
lambda x: x * x
</syntaxhighlight>
JavaScript.
JavaScript支持匿名函数。
<syntaxhighlight lang="javascript">
alert((function(x){
 return x*x;
})(10)); // 提示100
</syntaxhighlight>
小书签也经常使用这种结构，例如下面的一个小书签就将当前网页的标题显示为其URL：
<syntaxhighlight lang="javascript">
javascript:document.title=location.href;
</syntaxhighlight>
然而，由于该赋值语句返回了一个值（即URL本身），很多浏览器会同时创建一个新的页面显示这个值。
取而代之，下面的匿名函数就可以做到不返回任何值：
<syntaxhighlight lang="javascript">
javascript:(function(){document.title=location.href;})();
</syntaxhighlight>
第一对圆括号中的函数（“(function(){document.title=location.href;})”）用作声明一个匿名函数，而最后的一对圆括号则用来执行这个函数。同等用法有：
<syntaxhighlight lang="javascript">
javascript:var f = function(){document.title=location.href;}; f();
</syntaxhighlight>
PHP.
PHP 4.0.1之前不支持匿名函数。
4.0.1 至 5.3.
PHP 4.0.1新增加了codice_13函数，这是匿名函数的雏形。该函数能创建一个随机命名的新函数并以字符串形式返回新函数的函数名。
<syntaxhighlight lang="php">
$foo = create_function('$x', 'return $x*$x;');
$bar = create_function("\$x", "return \$x*\$x;");
echo $foo(10);
</syntaxhighlight>
要注意的是，新函数本身及其变量都要放在单引号里面，如果要放在双引号之内，美元符号“$”则需要转码成为“\$”。
5.3.
PHP 5.3新增加了codice_14类，以及能使类的实例可被调用的“魔术方法”codice_15。Lambda函数都是编译器的一种“花招”，它能产生新的能被调用的codice_14实例，就像函数能被调用一样。
<syntaxhighlight lang="php">
$x = 3;
$func = function($z) { return $z *= 2; };
echo $func($x); // 输出结果为6
</syntaxhighlight>
上述例子中的codice_17是codice_14类的一个实例，而codice_19则相当于是codice_20。PHP 5.3模仿使用匿名函数，但并非支持真匿名函数，因为PHP的函数仍非第一类函数。
虽然PHP 5.3支持闭包，但还需要像这样明确标识其变量：
<syntaxhighlight lang="php">
$x = 3;
$func = function() use(&$x) { $x *= 2; };
$func();
echo $x; // 输出结果为6
</syntaxhighlight>
codice_17引用了变量codice_22（&$x），在调用的时候就会修改原来的$x，其结果在函数以外的地方也是可见的。
C++.
C++ 98/03.
C++ 98/03标准并不原生支持匿名函数。不过可以利用Boost库的Boost.Lambda来实现一个匿名函数。
C++ 11.
C++11标准提供了匿名函数的支持，在《ISO/IEC 14882:2011》（C++11标准文档）中叫做lambda表达式。一个lambda表达式有如下的形式：
<syntaxhighlight lang="cpp">
</syntaxhighlight>
必须用方括号括起来的capture列表来开始一个lambda表达式的定义。
lambda函数的形参表比普通函数的形参表多了3条限制：
如果lambda函数没有形参且没有mutable、exception或attribute声明，那么参数的空圆括号可以省略。但如果需要给出mutable、exception或attribute声明，那么参数即使为空，圆括号也不能省略。
如果函数体只有一个return语句，或者返回值类型为void，那么返回值类型声明可以被省略：
<syntaxhighlight lang="cpp">
</syntaxhighlight>
一个lambda函数的例子如下：
<syntaxhighlight lang="cpp">
[](int x, int y) { return x + y; } // 从return语句中隐式获得的返回值类型
[](int& x) { ++x; } // 没有return语句 -> lambda函数的返回值为void
[]() { ++global_x; } // 没有参数，仅仅是访问一个全局变量
[]{ ++global_x; } // 与前者相同，()可以被省略
</syntaxhighlight>
在上面的第一个例子中这个无名函数的返回值是codice_23。如果lambda函数体的形式是codice_24，或者甚么也没返回，或者所有返回语句用codice_25都能检测到同一类型，那么返回值类型可以被省略。
返回值类型可以显式指定，如下所示：
<syntaxhighlight lang="cpp">
</syntaxhighlight>
在这个例子中，一个临时变量，codice_26，被创建来储存中间过程。与一般的函数一样，中间值在调用的前后并不存在。甚么也没有返回的lambda表达式无需显式指定返回值，没有必要写codice_27代码。
lambda函数可以捕获lambda函数外的具有automatic storage duration的变量，即函数的局部变量与函数形参变量。因而，具有static storage duration的变量不能被lambda捕获，只能直接使用，这包括静态局部变量。函数体与这些变量的集合合起来称做闭包。这些外部变量在声明lambda表达式时列在在方括号codice_28和codice_29中。空的方括号表示没有外界变量被capture或者按照默认方式捕获外界变量。这些变量被传值捕获或者引用捕获。对于传值捕获的变量，默认为只读（这是由于lambda表达式生成的为一个函数对象，它的codice_30成员缺省有const属性）。修改这些传值捕获变量将导致编译报错。但在lambda表达式的参数表的圆括号后面使用mutable关键字，就允许lambda函数体内的语句修改传值捕获变量，这些修改与lambda表达式（实际上是用函数对象实现）有相同的生命期，但不影响被传值捕获的外部变量的值。lambda函数可以直接使用具有static存储期的变量。如果在lambda函数的捕获列表中给出了static存储期的变量，编译时会给出警告，仍然按照lambda函数直接使用这些外部变量来处理。因此具有static存储期的变量即使被声明为传值捕获，修改该变量实际上直接修改了这些外部变量。编译器生成lambda函数对应的函数对象时，不会用函数对象的数据成员来保持被“捕获”的static存储期的变量。示例：
<syntaxhighlight lang="cpp">
[] // 没有定义任何变量，但必须列出空的方括号。在Lambda表达式中尝试使用任何外部变量都会导致编译错误。
&y // x是按值传递，y是按引用传递
[&] // 任何被使用到的外部变量都按引用传入。
[=] // 任何被使用到的外部变量都按值传入。
[&, x] // x按值传入。其它变量按引用传入。
[=, &z] // z按引用传入。其它变量按值传入。
</syntaxhighlight>
下面这个例子展示了lambda表达式的使用：
<syntaxhighlight lang="cpp">
std::vector<int> some_list{ 1, 2, 3, 4, 5 };
int total = 0;
std::for_each(begin(some_list), end(some_list), 
</syntaxhighlight>
在类的非静态成员函数中定义的lambda表达式可以显式或隐式捕捉codice_31指针，从而可以引用所在类对象的数据成员与函数成员。对象的this指针必需显式捕获声明。因此，被捕获的类的数据成员总是用this指针来访问，如果this所指的对象不存在，则this是空悬指针。解决办法是在定义lambda之前用局部变量复制一份数据成员的值，然后捕获这个局部变量的值。
lambda函数的函数体中，可以访问下述变量：
lambda函数的数据类型是函数对象，保存时必须用codice_32模板类型或codice_33关键字。
例如：
<syntaxhighlight lang="cpp">
double eval(std::function <double(double)> f, double x = 2.0)
 return f(x);
int main()
 std::function<double(double)> f0 = [](double x){return 1;};
 auto f1 = [](double x){return x;};
 decltype(f0) fa= {f0,f1,[(double x){return x*x;}};
 std::vector<decltype(f0)> fv = {f0,f1};
 fv.push_back ([](double x){return x*x;});
 for(int i=0;i<fv.size();i++)
 std::cout Â« fv(2.0) Â« std::endl;
 for(int i=0;i<3;i++)
 std::cout Â« fa(2.0) Â« std::endl;
 for(auto &f : fv)
 std::cout Â« f(2.0) Â« std::endl;
 for(auto &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;
</syntaxhighlight>
一个lambda函数的捕捉表达式为空，则可以用普通函数指针存储或调用。例如：
<syntaxhighlight lang="cpp">
auto a_lambda_func = [](int x) { /*...*/ };
void (* func_ptr)(int) = a_lambda_func;
func_ptr(4); //calls the lambda.
</syntaxhighlight>
C++14.
C++11中捕获机制的局限
C++14增加了广义捕获(Generalized capture)，或称“初始化捕获”。即在捕获子句（capture clause）中增加并初始化新的变量，该变量不需要在lambda表达式所处的闭包域（enclosing scope）中存在；即使在闭包域中存在也会被新变量覆盖(override)。新变量类型由它的初始化表达式推导。也就是说可以创建新的变量并在捕获子句中对其进行初始化。这种方式称之为带有初始化程序的捕获或者广义lambda捕获。一个用途是可以从闭包域中捕获只供移动的变量并使用它。形如= localVar1, mVar2 = std::move(localVar2)(){};
C++14还允许lambda函数的形参使用codice_33关键字作为其类型，这实质上是函数对象的codice_30成员作为模板函数；并且允许可变参数模板。
<syntaxhighlight lang="cpp">
auto a_lambda_func = [data1=101](int x) { /*...*/ }; //广义捕获，实质上是在函数对象中增加了数据成员data1并初始化
auto ptr = std::make_unique<int>(10); //See below for std::make_unique
auto lambda3 = [](auto x, auto y) {return x + y;} //lambda函数的形参类型为auto
struct unnamed_lambda //这相当于函数对象：
auto lambda4 = [](auto&&... params) //可变参数的函数模板
</syntaxhighlight>
Visual Basic.NET.
匿名函数或lambda表达式即无名的函数或过程，作为表达式的值。可以写为一行或多行。例如：
<syntaxhighlight lang="vb.net">
</syntaxhighlight>
可以在声明匿名函数的同时调用它。单行的lambda表达式不能使用Return关键字，其返回类型是自动推导得出；其参数要么都是用As关键字指明类型，要么全部是自动推导出类型。
lambda表达式在定义时可以使用所在上下文（context，即C++语言的闭包closure）的局部变量、参数、属性、Me等等的值，即使lambda表达式离开了定义时所在的context，这些被使用的局部变量等的值仍然有效。这是因为lambda表达式在定义时把所用到的context的值保存到它自己的定义类中。lambda表达式可以嵌套定义。