C++ 11 對LB的支持,對於喜歡Functional Programming的人來說,無疑是超好消息。它使得C++進入了和C#,JavaScript等現代流行的程序設計語言所代表的名人堂。
不熟悉LB本身的網友,可以看MSDN文章
( http://msdn.microsoft.com/en-us/library/dd293608.aspx ),我僅僅簡單地分析一下VC++中LB的用法,實現,和性能。
對於一次性的,帶參數表達式,用LB可以節省不必要的class定義和維護,簡化程序的設計-維護代價。
比如下面的vector處理代碼,簡潔明了:
vector<int> v1(10, 1);
int sum = 0;
for_each (v1.begin(), v1.end(), [&](int i){ sum += i; });//Line1
否則,我們必須定義一個function類,把如此簡單的事情復雜化。用了LB,我們把定義function 類的工作,轉交給編譯。VC++中,上述LB編譯的實現是產生一個隱身類:
class _lambda_a01 {
int &capture1_;
public:
_lambda_a01(int &x): capture1_(x) {} //Line2
operator void (int i) { capture1_ += I; }
};
在引用時(Line1),它變成:
_lambda_a01 lbd1(sum);
for(auto a:v1){
ldb1(a);
}
讀者也許好奇,為什麼C++不直接把LB轉換成inline expression (inline 表達式),而是要生成一個隱身類呢?這是因為LB的確可以當成“type”變量來用,這樣使得LB和其他類有了同等地位。比如:
vector<int> v1(10, 1);
int sum = 0;
for_each (v1.begin(), v1.end(), [&](int i){ sum += i; });//Line1
vector<int> v2(10, 1);
int sum2 = 0;
for_each (v1.begin(), v1.end(), [&](int i){ sum2 += i; });//Line2
我們如果用上述的方法,Line1和Line2重復代碼,是軟件工程的大忌。我們可以用下列LB使用模式:
vector<int> v1(10, 1);
vector<int> v2(10, 1);
int sum = 0;
auto lb = [&](int i){ sum += i; }; //Line0
for_each (v1.begin(), v1.end(), lb);//Line1
sum = 0; // Line1.1
for_each (v1.begin(), v1.end(), lb});//Line2
在Line0,我們定義了一個有名(lb)無型的LB,可以在Line1和Line2重復使用。
注意的是,
1) 每個LB的“定義”都會產生新的“隱身”類,所以盡量用“有名引用”,會減少代碼的size,縮小工作集。
2) 定義時,LB一次性“俘獲”環境變量,所以上面修改後的代碼加了Line1.1,以便正確表達應用邏輯。
3) 俘獲可以是“傳值(by value)”也可以是“傳引用(by reference)。我們Line0用的是by reference.
上面兩種LB使用模式,是LB應用的主要模式,它直接反映出了LB的優點。另一方面說,既然LB無非是隱身類,我們沒有理由不能把它當作普通變量使用。這個模式是一種簡化的functor使用模式。我們可以把LB定義成一個std::function,比如上面的auto lb可以定義成:
std::function <void(int)> lb; //lb is a function which takes an integer and returns void
注意到用這個定義,使得我們可以推遲給LB變量賦值,甚至一變量賦多址(不同時間)。下面就是一個簡單用例:
struct MyLambda
{
std::function <int (int)> _lbda;//line1
int _extra;
};
MyLambda TestLambdaObj(int t)
{
MyLambda ret;
if (t == 1)
{
ret._extra = t;
ret._lbda = [=](int x) -> int { return t + x; }; //line2
return ret;
}
else
{
ret._extra = t;
ret._lbda = [=](int x) -> int { return t * x; };//line3
return ret;
}
}
void TestLambdaFun2(int t)
{
MyLambda ret = TestLambdaObj(t);
int v = ret._lbda(t); //line4
printf("v is '%d' for type %d", v, t);
}
我們先定義MyLambda數據類,並與其定義了一了function成員_lbda,根據C++ SPEC,他可以由LB轉換構造,並且和普通的類變量無甚區別。然後我們可以運行時給它賦值(line2,line3), 當作普通function來使用(line4)。
注意的是:
如果你問為什用LB而不用std::function?我的回答是“閉包”。
C++用LB來實現閉包,是一個簡化繁瑣的class初始化的syntax sugar。這一點是std::function所不可替代的。比如說:
auto sum = 0;
auto step = 2;
auto lb = [&](int i){ sum += i + step; }//capture sum and step by ref
lb形成自己的閉包,自動從環境中俘獲了sum和step,若用class實現,上面的程序起碼增加10行代碼。
下面的簡單程序,測試四種功能完全一樣,但使用不同表達式的邏輯:
1)t =1 時用LB,
2)t=2 時用直接表達式
3)t=3 時用函數
4)t=4時用std::function間接調用LB
void TestLambdaFun(int t)
{
using namespace std;
vector<int> v1(10, 1);
int x = 0;
int u = 0;
if (t == 1)
{
clock_t begin = clock();
for (int i = 0; i < 100000; ++i)
{
for_each (v1.begin(),
v1.end(),
[&x, &u](int i){ u += i+(x++); });// Line 1
}
clock_t end = clock();
auto spent = double(end - begin) / CLOCKS_PER_SEC;
printf("spent for type '%d' is %f u is %d\n", t, spent, u);
}
else if (t == 2)
{
clock_t begin = clock();
for (int i = 0; i < 100000; ++i)
{
auto _First = v1.begin();
auto _Last = v1.end();
for (; _First != _Last; ++_First)
{
u = *_First+(x++); // Line 2
}
}
clock_t end = clock();
auto spent = double(end - begin) / CLOCKS_PER_SEC;
printf("spent for type '%d' is %f u is %d\n", t, spent, u);
}
else if (t == 3)
{
clock_t begin = clock();
for (int i = 0; i < 100000; ++i)
{
auto _First = v1.begin();
auto _Last = v1.end();
for (; _First != _Last; ++_First)
{
FuncAdd(u, x, *_First); // Line 3
}
}
clock_t end = clock();
auto spent = double(end - begin) / CLOCKS_PER_SEC;
printf("spent for type '%d' is %f u is %d\n", t, spent, u);
}
else if (t == 4)
{
clock_t begin = clock();
std::function <void (int)> lbda;
for (int i = 0; i < 100000; ++i)
{
lbda = [&](int i){ u += i + (x++); };
for_each (v1.begin(), v1.end(), lbda); // Line 4
}
clock_t end = clock();
auto spent = double(end - begin) / CLOCKS_PER_SEC;
printf("spent for type '%d' is %f u is %d\n", t, spent, u);
}
}
void FuncAdd(int &u, int &x, int i)
{
u = i+(x++);
}
下面是VC++ 2010中的測試結果:
t=4最慢,它和t=3類似。但是由於通過了std::function的虛擬函數表間接調用,/Ob1優化失去作用,使它不但要調用一個() operator,而且是通過“虛擬表”間接調用。所以從性能上說,把LB通過std::function間接使用,失去了LB的性能優勢。
C++ 11 的lambda expression(簡稱LB),在可以保證和inline expression同樣性能的條件下,增加了參數功能和閉包功能,是我們寫出簡潔,明了,易維護代碼的絕佳工具。應用時,為了避免代碼重復和增加隱身類的數量,可用有名無型的LB變量。LB也可以賦值於std::function,當作函數指針使用,但是性能不及簡單地inline使用。
相當於是這樣的
class test
{
test(Racer r, Car c)
{
this.Racer = r;
this Car = c;
}
Racer{get;set;}
Car {get;set;}
}
test t = new test(r,c);
八竿子打不著的關系
lambda表達式是函數式語言的特性,c#利用自身動態編譯的特性才實現這種效果的,可以用它寫匿名函數等.像C,C++神馬的都沒有的.
正則是各語言都有實現的,用來查詢匹配等的