常用編程語言對Lambda表達式的支持
Lambda表達式,也稱為匿名函數,是一種無需定義名稱的函數或子程序,在很多高級語言中普遍存在。1958年LISP首先采用匿名函數,發展至今,越來越多的編程語言開始支持該特性,包括C++, PHP等,本文列舉了常用的編程語言對lambda表達式的支持,增強對lambda表達式的認識,并了解不同是如何支持lambda表達式的。
Java 8
2013年Java 8的發布,宣布java對lambda表達式的支持,在java支持lambda表達式之前,我們必須這樣寫代碼:
button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ui.dazzle(e.getModifiers()); } });
你還需要導入java.awt.event.ActionListener功能接口。而java8帶來了lambda表達式,我們只需這樣寫:
button.addActionListener(e -> { ui.dazzle(e.getModifiers()); });
編譯器知道lambda表達式符合void actionPerformed(ActionEvent)方法的定義,看起來lambda實體返回的是void,實際上編譯器能推斷出參數e的類型就是java.awt.event.ActionEvent。
java 8的lambda表達式的出現主要是大大簡化了功能接口的編寫,同時java8還提供了java.util.functions包,包含了很多新的功能接口和迭代方法,這些功能都分成類似于LINQ。
C++ 11
c++ 11也開始支持labmda表達式來,C++ 11的lambda表達式的格式如下:
[capture](parameters) mutable or exception->return-type {body}
[capture]表示一個Lambda表達式的開始,這部分是必須的,不能省略。capture是傳遞給編譯器自動生成的函數對象類的構造函數,capture只能使用那些定義當前lambda表達式之前所定義的,并且在lambda作用范圍內可見的局部變量(包含lambda所在類的this),包含以下形式:
- 空, 不使用該參數;
- =。函數體內可以使用Lambda所在作用范圍內所有可見的局部變量(包括Lambda所在類的this),并且是值傳遞方式(相當于編譯器自動為我們按值傳遞了所有局部變量)。
- &。函數體內可以使用Lambda所在作用范圍內所有可見的局部變量(包括Lambda所在類的this),并且是引用傳遞方式(相當于編譯器自動為我們按引用傳遞了所有局部變量)。
- this。函數體內可以使用Lambda所在類中的成員變量。
- a。將a按值進行傳遞。按值進行傳遞時,函數體內不能修改傳遞進來的a的拷貝,因為默認情況下函數是const的。要修改傳遞進來的a的拷貝,可以添加mutable修飾符。
- &a。將a按引用進行傳遞。
- a, &b。將a按值進行傳遞,b按引用進行傳遞。
- =,&a, &b。除a和b按引用進行傳遞外,其他參數都按值進行傳遞。
- &, a, b。除a和b按值進行傳遞外,其他參數都按引用進行傳遞。
parameters表示重載的()操作符參數,沒有參數時,可以省略。參數可以按值(如:(a, b)),也可以按引用(如:(&a, &b))傳遞參數。
mutalbe或exception聲明可以省略,按值傳遞[capture]參數時,加上mutalbe修飾符,可以修改參數拷貝的值(修改的是值類型的拷貝)。exception聲明用于指定函數拋出的異常,如拋出整數類型的異常:throw(int)。
“-> return-type”,表示函數返回值類型,當沒有返回值時,或者函數體內只有一處return語句(編譯器可以推斷出返回值的類型),該部分就可以省略。
{body}表示函數的實現,函數體可以空,當花括號不能省略。
看一個例子,該例子是統計字符串中的大寫字母,使用for_each遍歷字符串,使用lambda表達式檢查字母是否為大寫,如果是大學,uppercase加1:
int main() { char s[]="Hello World!"; int uppercase = 0; //modified by the lambda for_each(s, s+sizeof(s), [&uppercase] (char c) { if (isupper(c)) uppercase++; }); cout<< Uppercase<<" uppercase letters in: "<< s<
uppercase是定義在lambda表達式外的一個變量,并且聲明在lambda表達式之前,[&uppercase]中的“&”表示lambda函數體中使用的uppercase為一個應用類型,以便更新大寫字母的個數。
Objective-C
下面的例子,分別是Objective-C和C++ 11的Lambda表達式寫法:
^{ printf("Hello, World!\n"); } ();
[] { cout << "Hello, World" << endl; } ();
在創建Objective-C的lambda表達式時,在語法上是以“^”開始,而C++ 11是以“[]”開始。
實際上Objective-C的lambda表達式是構建在C語言之上的,也稱為block,是C語言的擴展特性,如果你理解C語言的函數指針,那么有很容易理解Objective-C的block了,只是語法稍微不同。如下一個block的原型:
int (^maxBlk)(int , int);
除了“^”外,是不是和C語言的函數指針的聲明一樣。上面的示例就是聲明一個block原型,名字為maxBlk,帶有兩個個int參數,返回值為int類型。
Objective-C有了Lambda表達式后,就可以很容易的定義一個滿足maxBlk簽名的函數,并且這個函數是匿名的。如下:
maxBlk = ^(int m, int n){ return m > n ? m : n; };
你也可以這樣寫:
int (^maxBlk)(int , int) = ^(int m, int n){ return m > n ? m : n; };
看如下Objective-C的代碼,包含了3給block:
#import int (^maxBlk)(int , int) = ^(int m, int n){ return m > n ? m : n; }; int main(int argc, const char * argv[]) { ^{ printf("Hello, World!\n"); } (); int i = 1024; void (^blk)(void) = ^{ printf("%d\n", i); }; blk(); return 0; }
C#
C#早在2.0版本就引入了匿名方法,簡化了我們編寫事件處理函數的工作,使我們不再需要單獨聲明一個函數來綁定事件,只需要delegate關鍵字就可以編寫事件處理代碼了。如下:
// Create a handler for a click event button1.Click += delegate(System.Object o, System.EventArgs e) { System.Windows.Forms.MessageBox.Show("Click!"); };
而C#3.0再更進一步地推出了Lambda表達式,使得編寫事件處理函數更加簡潔,lambda表達式更新一個計算表達式,使用“=>”符合來連接事件參數和事件處理代碼。如:
button1.Click += (o, e) => { System.Windows.Forms.MessageBox.Show("Click!"); };
在C#中,事件處理函數其實就是委托。委托除了可以作為事件處理函數,還可以作為函數指針,或回調函數使用,都可以使用lambda表達式的寫法。如:
delegate int del(int i); del myDelegate = x => x * x; int j = myDelegate(5); //j = 25
或者:
Func myFunc = x => x == 5; bool result = myFunc(4); // returns false of cour
另外Lambda表達式在LINQ中發揮了重要作用。
PHP 5.3
PHP5.3增加了Lambda的支持,對于接受回調函數的PHP函數來說,lambda表達式非常方便。比如使用array_map函數遍歷數組,并將回調結果重新賦值給數字各元素。在早期PHP版本中,我們在調用array_map之前,必須事先定義好回調函數,比如:
function quoteWords() { if (!function_exists ('quoteWordsHelper')) { function quoteWordsHelper($string) { return preg_replace('/(\w)/','"$1"',$string); } } return array_map('quoteWordsHelper', $text); }
現在PHP5.3對lambda表達式的支持,使得編碼根據簡單。如下使用lambda表達式的實現:
function quoteWords() { return array_map('quoteWordsHelper', function ($string) { return preg_replace('/(\w)/','"$1"',$string); }); }
Python
Python世界的lambda表達式根據簡單,只是在python世界里,lambda表達式主要適用比較小的函數。如普通Python的函數定義:
def func(x): return x * 2 func(2) # 4
使用lambda表達式的寫法:
func = lambda x:x*2 func(2) # 4
python的lambda表達式常用在一些回調中,比如:
a = [1, 2, 3, 4] map(lambda x:x**2, a) # output: [1, 4, 9, 16]
或者
a = [1,2,3,4,5,6,7,8,9] low, high = 3, 7 filter(lambda x:high>x>low, a) # output: [4,5,6]
"x**2"表示x的平方。"high>x>low"等于“high>x and x > low”。
Javascript
javascript中的lambda表達式通常稱為匿名函數,如果你使用過jquery庫,那么你肯定知道匿名函數,這里主要作為回調函數使用。比如:
$('#submit').on('click', function(){ functionbody. })
其中方法on的第二個參數就是匿名函數,javascript中的你們函數還有其他存在形式,比如:
var func = new Function('x', 'return 2*x;');
或者
var func = function(x) { return 2*x; }
還有就是很多js庫常用的方式,表示創建匿名函數,并調用之:
(function(x, y) { alert(x+y); })(2, 3);
總結
lambda表達式的出現簡化了編碼量,多用于函數回調、事件、匿名函數,并且與閉包的結合使用,更能發揮強大的作用。另外,支持lambda表達式的編程語言有很多,這里主要介紹了我相對熟悉的幾種。
出處:http://guangboo.org/2014/01/16/lambda-supported-programming-language