淺析匿名函數、lambda表達式、閉包(closure)區別與作用
所有的主流編程語言都對函數式編程有支持,比如c++11、python和java中有lambda表達式、lua和JavaScript中有閉包(closure)、ObjectC中有塊(blocks,^)。那么這幾個概念有什么共性和區別呢,只用普通函數不行嗎,為什么要創造這些炫酷的概念呢?
一.匿名函數、lambda、closure區別
從功能性上說lambda和closure(或是OC中的blocks)是一個東西,只是不同語言的不同稱呼罷了,它們都是匿名函數。若匿名函數捕獲了一個外部變量,那么它就是一個closure。
二.匿名函數、lambda、closure作用
簡單說,引入它們的作用有2個:
- 簡潔
- 捕獲變量
首先說簡潔,匿名函數可以在其他函數內部聲明與定義,不用另外寫個命名函數,舉個栗子,顯示c++vector中每個值,若不使用匿名函數,代碼是這樣的
1 #include <iostream> 2 #include <vector> 3 #include <algorithm> 4 using namespace std; 5 6 void show(int & value) { 7 std::cout << "value " << value << std::endl; 8 } 9 10 int main() { 11 vector<int> * v = new vector<int>(3, 1); 12 13 for_each(v->begin(), v->end(), show); 14 15 return 0; 16 }
若使用匿名函數是這樣的:
1 #include <iostream> 2 #include <vector> 3 #include <functional> 4 #include <algorithm> 5 using namespace std; 6 7 int main() { 8 vector<int> * v = new vector<int>(3, 1); 9 10 for_each(v->begin(), v->end(), [](int & v){ 11 std::cout << "value " << v << std::endl; 12 }); 13 14 return 0; 15 }
什么是捕獲變量呢?就是讓匿名函數可以使用匿名函數外定義的變量,但是匿名函數內的函數外變量是外部變量的一個clone(C++11可以捕獲引用,不是clone。OC中有__block,也可以直接改變外部變量的值),在匿名函數內修改(諾能)外部變量不會影響到外部變量。可以說closure就是函數+捕獲的變量。以lua為例:
1 local intValue = 10; 2 3 local func = function (p) 4 intValue = intValue + p; 5 return intValue; 6 end 7 8 print(func(3)); 9 print(func(3)); 10 print(intValue)
打印的結果為:13,16,10。可見匿名函數的外部變量被捕獲到了func中。
三.匿名函數、lambda、closure在各個語言中的使用方式:
1.ObjectC:
在ObjectC中,匿名函數被稱為blocks(塊),即可以改變捕獲的原值、又可以捕獲克隆、但不能改變克隆值的值。捕獲並改變外部值,需要用__block,否則復制語句會報錯,使用代碼如下:
1 #import <Foundation/Foundation.h> 2 3 int main(int argc, const char * argv[]) { 4 @autoreleasepool { 5 __block int foo = 10; 6 7 int (^blockFunc)(int p) = ^(int p) { 8 foo += p; 9 return foo; 10 }; 11 12 NSLog(@"%d", blockFunc(4)); 13 14 NSLog(@"%d", foo); 15 } 16 return 0; 17 }
2.C++:
C++中匿名函數被稱為lambda,即可以改變捕獲的原值、又可以捕獲克隆、又可以改變克隆值的值,語法形式可以簡單歸納如下:
[capture](params)ops->ret{body;}
capture是捕獲列表,params是參數表,opt是可選選項,ret是返回值類型,body是函數體。具體怎么使用可以參考C++11 lambda 表達式解析和C++11 lambda 表達式。
3.lua:
lua中的匿名函數被稱為閉包(closure),只能捕獲和改變原值的克隆,不能改變原值(table除外)
4.python:
python中的匿名函數被稱為lambda,只能捕獲克隆值,且不能改變他。