匿名とくめい函数かんすう

本页使用了标题或全文手工转换
维基百科ひゃっか自由じゆうてき百科ひゃっかぜん

匿名とくめい函数かんすう英語えいごAnonymous Functionざい计算つくえ编程ちゅうゆび一类无需定义标识函数かんすうめいてき函数かんすうあるほどじょ普遍ふへん存在そんざい于多种编ほど语言ちゅう

1958ねんLISPくびさきさいよう匿名とくめい函数かんすう此之きさき越来ごえくえつ编程语言陆续さいよう主流しゅりゅうてき编程语言如PHP[1]C++[2]也陸ぞくさいよう

用途ようと[编辑]

はいじょ[编辑]

尝试はた类按名称めいしょうはいじょ

a = [10, '10', 10.0]
a.sort(lambda x,y: cmp(x.__class__.__name__, y.__class__.__name__))
print a
[10.0, 10, '10']

上述じょうじゅつ 10.0 てき类名float”,10 てき类名int”而 '10' てき类名str”,排列はいれつきさきてき顺序为“float”“int”,接着せっちゃくstr”。

该示れいちゅうてき匿名とくめい函数かんすう就是lambdaひょう达式:

lambda x,y: cmp(...)

该匿めい函数かんすう接受せつじゅ两个变量 x yつう过内函数かんすう cmp() かえしかい两者てき较值,下面かめんてきれいはた按长为字くしれつひょうはいじょ

a = ['three', 'two', 'four']
a.sort(lambda x,y: cmp(len(x), len(y)))
print a
['two', 'four', 'three']

语言れつひょう[编辑]

语言 支持しじ 备注
ActionScript
C ざいゆうclangかずllvmてきcompiler-rtほどじょ库的环境支援しえん
C# したがえC# 3.0 (.Net Farmework 3.5)開始かいし支援しえん
C++ C++11开始支援しえん,标准中称ちゅうしょうlambdaひょう达式(lambda expression)[3][2]
Clojure
Curl
D
Delphi 从Delphi 2009开始支援しえん
Dylan
Erlang
F#
Frink
Go
Haskell
Java Java 8开始支援しえん[4]
JavaScript
Kotlin
Lisp
Logtalk
Lua
Mathematica
Matlab
ML语言
(Objective Caml, Standard ML, etc.)
Octave
Object Pascal 原生げんせい支援しえん匿名とくめい函数かんすう,其正しき名称めいしょう为“匿名とくめい方法ほうほう”(anonymous method)。Oxygene Object Pascal也支持しじ匿名とくめい函数かんすう
Objective-C (Mac OS X 10.6+) しょうさく“块”(block)
Pascal
Perl
PHP 从PHP 5.3.0开始支援しえん匿名とくめい函数かんすうこれぜん则只支持しじ部分ぶぶん匿名とくめい函数かんすう
Python Pythonようlambda语法定ほうてい义匿めい函数かんすうただ需用じゅようひょう达式而无需声明せいめい
R
Ruby Rubyてき匿名とくめい函数かんすうげんSmalltalk,也同样叫“块”(block)。
Rust Rustてき匿名とくめいはこしき使用しよう閉包へいほう”(Closures)らい實現じつげん
Scala
Scheme
Smalltalk Smalltalkてき匿名とくめい函数かんすうしょう为“块”(block)
Tcl
Visual Basic .NET v9
Visual Prolog v 7.2
Vala

しめせれい[编辑]

Python[编辑]

Pythonようlambda语法定ほうてい义匿めい函数かんすうただ需用じゅようひょう达式而无需声明せいめい

# 以下いか两种相等そうとうどう
# 1.使用しよう匿名とくめい函数かんすう
def f(x):
	return x * x
# 2.使用しよう匿名とくめい函数かんすう
lambda x: x * x

JavaScript[编辑]

JavaScript支持しじ匿名とくめい函数かんすう

alert((function(x){
	return x*x;
})(10)); // 提示ていじ100

しょう书签也经つね使用しよう这种结构,れい如下めんてき一个小书签就将当前网页的标题显示为其URL:

javascript:document.title=location.href;

しか而,ゆかり于该赋值语句かえしかいりょういち个值(そくURL本身ほんみ),很多浏览会同かいどう时创けん一个新的页面显示这个值。

而代下面かめんてき匿名とくめい函数かんすう就可以做いたかえしかいにんなん值:

javascript:(function(){document.title=location.href;})();

だい一对圆括号中的函数(“(function(){document.title=location.href;})”)用作ようさく声明せいめいいち个匿めい函数かんすう,而最きさきてき一对圆括号则用来执行这个函数。同等どうとう用法ようほうゆう

javascript:var f = function(){document.title=location.href;}; f();

PHP[编辑]

PHP 4.0.1ぜん支持しじ匿名とくめい函数かんすう[5]

4.0.1 いたり 5.3[编辑]

PHP 4.0.1しん增加ぞうかりょうcreate_function函数かんすう,这是匿名とくめい函数かんすうてき雏形。该函すうのう创建いち个随つくえ命名めいめいてきしん函数かんすう并以くし形式けいしきかえしかいしん函数かんすうてき函数かんすうめい

$foo = create_function('$x', 'return $x*$x;');
$bar = create_function("\$x", "return \$x*\$x;");
echo $foo(10);

要注意ようちゅういてきしん函数かんすう本身ほんみ及其变量ようざい单引ごうさとめん,如果ようざいそう引号これないよしもと符号ふごう“$”则需よう转码なり为“\$”。

5.3[编辑]

PHP 5.3しん增加ぞうかりょうClosure类,以及のう使类的实例调用てき术方ほう__invoke()[6]。Lambda函数かんすう编译てきいち种“はな招”[7],它能产生新せいしんてきのう调用てきClosure实例,就像函数かんすうのう调用いち样。

$x = 3;
$func = function($z) { return $z *= 2; };
echo $func($x); // 输出结果为6

上述じょうじゅつれい子中こなかてき$funcこれClosure类的いち个实れい,而echo $func()则相とう于是$func->__invoke($z)。PHP 5.3仿使用しよう匿名とくめい函数かんすうただし并非支持しじ匿名とくめい函数かんすういん为PHPてき函数かんすう仍非だいいち类函すう

虽然PHP 5.3支持しじ闭包,ただし需要じゅようぞう这样あかり确标识其变量:

$x = 3;
$func = function() use(&$x) { $x *= 2; };
$func();
echo $x; // 输出结果为6

$func引用いんようりょう变量$x(&$x),ざい调用てき时候就会おさむあらためばららいてき$x,其结はてざい函数かんすう以外いがいてき地方ちほう也是见的。

C++[编辑]

C++ 98/03[编辑]

C++ 98/03标准并不原生げんせい支持しじ匿名とくめい函数かんすう过可以利用りようBoost库的Boost.Lambdaらい实现いち个匿めい函数かんすう[8]

C++ 11[编辑]

C++11标准提供ていきょうりょう匿名とくめい函數かんすうてき支持しじざい《ISO/IEC 14882:2011》(C++11标准ぶん档)ちゅうさけべlambdaひょうたちしき[9]いちlambdaひょうたちしきゆう如下てき形式けいしき

[capture] (parameters) mutable exception attribute -> return_type { body }

必须ようかたくくごうくくおこりらいてきcaptureれつひょうらい开始いち个lambdaひょう达式てきてい义。

lambda函数かんすうてきかたちさんひょう普通ふつう函数かんすうてきかたちさんひょうりょう3じょうげんせい

  1. さんすう不能ふのうゆうかけしょう
  2. 不能ふのう有可ゆか变长さん数列すうれつひょう
  3. 不能ふのうゆう无名さんすう

如果lambda函数かんすうぼつ有形ゆうけいさん且没ゆうmutable、exceptionあるattribute声明せいめい麼参すうてきそら圆括ごう省略しょうりゃくただし如果需要じゅよう给出mutable、exceptionあるattribute声明せいめい么参すうそく使为空,圆括ごう不能ふのう省略しょうりゃく

如果函數かんすうたいただゆういちreturn語句ごくあるものかえしかい值類がたためvoid,麼返かい值類がた声明せいめい以被省略しょうりゃく

[capture](parameters){body}

いちlambda函數かんすうてきれい如下:

[](int x, int y) { return x + y; } // したがえreturn語句ごくちゅうかくれしき獲得かくとくてきかえしかい值類がた
[](int& x) { ++x; }   // ぼつゆうreturn語句ごく -> lambda函數かんすうてきかえしかい值為void
[]() { ++global_x; }  // ぼつゆうさんすう僅僅きんきん訪問ほうもんいち全局ぜんきょく變量へんりょう
[]{ ++global_x; }     // あずか前者ぜんしゃしょうどう,()以被省略しょうりゃく

ざい上面うわつらてきだい一個例子中這個無名函數的返回值是decltype(x+y)。如果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ひょうたちしきてき使用しよう

std::vector<int> some_list{ 1, 2, 3, 4, 5 };
int total = 0;
std::for_each(begin(some_list), end(some_list), 
                 [&total](int x) {  total += x; }
              );

ざい类的せい态成员函すうちゅうてい义的lambdaひょう达式以显しきある隐式捕捉ほそくthisゆび针,从而引用いんよう所在しょざい类对ぞうてきすうすえなり员与函数かんすうなり员。对象てきthisゆび针必需显しき声明せいめいよし此,获的类的すうすえなり员总ようthisゆび针来访问,如果thisしょゆびてき对象存在そんざい,则thisそら悬指针。かい决办ほうざいてい义lambdaまえよう局部きょくぶ变量复制一份数据成员的值,しかきさき获这个局变量てき值。

lambda函数かんすうてき函数かんすうたいちゅう以访问下じゅつ变量:

  • 函数かんすうさんすう
  • 局部きょくぶ声明せいめいてき变量
  • 类数すえなり员:要求ようきゅうlambdaひょう达式声明せいめいざい类成员函すうちゅう,对象てきthisゆび针必需显しき声明せいめい
  • 具有ぐゆうせい态存储期てき变量(如全きょく变量)。一般いっぱんじょう况下,lambdaようらい获局变量てき,如果よう其来获全きょく变量あるものしずか态变りょう么编译器かい报warning
  • 获的外部がいぶ变量
    • 显式获的变量
    • 隐式获的变量,使用しようだま认捕获模しき(传值ある引用いんようらい访问。

lambda函数かんすうてきすうすえ类型函数かんすう对象保存ほぞん时必须用std::functionばん类型あるauto关键れい如:

#include <functional>
#include <vector>
#include <iostream>

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[3] = {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[i](2.0) << std::endl;
	for(int i=0;i<3;i++)
		std::cout << fa[i](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;
}

いち个lambda函数かんすうてき捕捉ほそくひょう达式为空,则可以用普通ふつう函数かんすうゆび针存储或调用。れい如:

auto a_lambda_func = [](int x) { /*...*/ };
void (* func_ptr)(int) = a_lambda_func;
func_ptr(4); //calls the lambda.

C++14[编辑]

C++11ちゅう获机せいてき局限きょくげん

  1. lambda获的局部きょくぶ变量あるかたちさんかん按值还是按引用いんよう获,这些めいてきひだり值对ぞう。而右值对ぞう匿名とくめい对象,无法获。
  2. 按值捕获时,ひだり值是复制いた闭包ちゅうてき。如果获的对象个只うつり动类がたてき对象时,いん其无法被はっぴ复制,就会错。
  3. 如果获的对象如果一个占用内存较大的对象时,按值捕获显しか效率こうりつ很低。

C++14增加ぞうかりょう广义捕获(Generalized capture),あるしょうはつはじめ获”。[10]そくざい获子(capture clause)ちゅう增加ぞうか并初はじめしんてき变量,该变りょう需要じゅようざいlambdaひょう达式しょ处的闭包いき(enclosing scope)ちゅう存在そんざいそく使ざい闭包いきちゅう存在そんざい也会しん变量くつがえ盖(override)。しん变量类型よし它的はつはじめひょう达式推导。也就说可以创けんしんてき变量并在获子ちゅう对其进行はつはじめ。这种方式ほうしきしょう为带ゆうはつはじめほどじょてき获或しゃ广义lambda获。一个用途是可以从闭包域中捕获只供うつりてき变量并使用しよう它。かたち如[mVar1 = localVar1, mVar2 = std::move(localVar2)](){};

C++14还允许lambda函数かんすうてきかたちさん使用しようauto关键さく为其类型,这实质上函数かんすう对象てきoperator()なり员作为模ばん函数かんすう;并且まこと变参すうばん

auto a_lambda_func = [data1=101](int x) { /*...*/ }; //广义捕获,实质じょうざい函数かんすう对象ちゅう增加ぞうかりょうすうすえなり员data1并初はじめ

auto ptr = std::make_unique<int>(10); //See below for std::make_unique
auto lambda1 = [ptr = std::move(ptr)] {return *ptr;}
          //だい致等こう于:   
auto lambda2 = [ptr = std::make_unique<int>(10)] {return *ptr;}

auto lambda3 = [](auto x, auto y) {return x + y;} //lambda函数かんすうてきかたちさん类型为auto
struct unnamed_lambda                            //这相とう于函すう对象:
{
  template<typename T, typename U>
    auto operator()(T x, U y) const {return x + y;}
};

auto lambda4 = [](auto&&... params)             //变参すうてき函数かんすうばん
  {
     return (foo(std::forward<decltype(params)>(params)...));
  }

Visual Basic.NET[编辑]

匿名とくめい函数かんすうあるlambdaひょう达式そく无名てき函数かんすうある过程,さく为表达式てき值。以写为いちぎょうあるくだりれい如:

 Dim func1=Function(i As integer) i+10
 Dim action = sub()
   End Sub
 Dim func2 = Function()
   End Function

以在声明せいめい匿名とくめい函数かんすうてきどう时调よう它。单行てきlambdaひょう达式不能ふのう使用しようReturn关键,其返かい类型动推导得;其参すうよう么都ようAs关键ゆびあかり类型,よう么全动推导出类型。

lambdaひょう达式ざいてい义时使用しよう所在しょざい上下じょうげぶん(context,そくC++语言てき闭包closure)てき局部きょくぶ变量、さんすう属性ぞくせい、Meとうとうてき值,そく使つかいlambdaひょう达式离开りょうてい义时所在しょざいてきcontext,这些使用しようてき局部きょくぶ变量とうてき值仍しか有效ゆうこう。这是いん为lambdaひょう达式ざいてい义时所用しょよういたてきcontextてき保存ほぞんいた自己じこてきてい义类ちゅう。lambdaひょう达式以嵌套定义。

参考さんこう资料[编辑]

  1. ^ Anonymous functions. [2014-04-27]. (原始げんし内容ないようそん于2014-03-27) えい语). Anonymous functions, also known as closures, allow the creation of functions which have no specified name. They are most useful as the value of callback parameters, but they have many other uses. 
  2. ^ 2.0 2.1 5.1.2 Lambda expressions. ISO/IEC 14882:2011. [2014-04-27]. (原始げんし内容ないようそん于2014-04-27) えい语). Lambda expressions provide a concise way to create simple function objects. 
  3. ^ Lambda expressions and closures for C++ (PDF). V Samko; J Willcock, J Järvi, D Gregor, A Lumsdaine. 2006-02-26 [2010-06-01]. (原始げんし内容ないようそん (PDF)于2011-07-28). 
  4. ^ Java 7 Features. Sun Microsystems. 2010-02-09 [2010-11-21]. (原始げんし内容ないようそん于2012-02-07). 
  5. ^ http://php.net/create_function页面そん档备份そん互联网档あん) the top of the page indicates this with "(PHP 4 >= 4.0.1, PHP 5)"
  6. ^ そん副本ふくほん. [2011-08-13]. (原始げんし内容ないようそん于2011-07-24). 
  7. ^ そん副本ふくほん. [2011-08-13]. (原始げんし内容ないようそん于2011-07-24). 
  8. ^ Chapter 16. Boost.Lambda - 1.55.0. [2014-04-27]. (原始げんし内容ないようそん于2014-04-27) えい语). 
  9. ^ 该标じゅんぶん档可以在ISO/IEC 14882:2011 - Information technology -- Programming languages -- C++页面そん档备份そん互联网档あん)购买,あるもの参看さんかん2012ねん一月份的标准草案文件N3337页面そん档备份そん互联网档あん),だい5.1.2节的Lambda expressions
  10. ^ MSDN: In C++14, you can introduce and initialize new variables in the capture clause, without the need to have those variables exist in the lambda function’s 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 C++14 you can capture move-only variables (such as std::unique_ptr) from the surrounding scope and use them in a lambda.

外部がいぶ链接[编辑]